使用要创建的类的名称设置类的属性

时间:2015-01-11 18:30:08

标签: python django django-contenttypes

我正在尝试允许多个模型使用Review提供的通用关系与django.contrib.contenttypes模型建立连接。

在Django 1.7中使用django.contrib.contenttypes.fields.GenericRelation,您可以在related_query_name上设置GenericRelation,以便在关系中执行反向查询。 The django docs give this example

class Bookmark(models.Model):
    url = models.URLField()
    tags = GenericRelation(TaggedItem, related_query_name='bookmarks')

>>> # Get all tags belonging to books containing `django` in the url
>>> TaggedItem.objects.filter(bookmarks__url__contains='django')
[<TaggedItem: django>, <TaggedItem: python>]

我想创建一个内置于模型中的装饰器,因此我不必为related_query_name='bookmarks'模型定义Bookmark。这可以用装饰器完成吗?我在想的是这样的:

# reviews/decorators.py

from django.contrib.contenttypes.fields import GenericRelation
from .models import Review

def reviewable(klass):
    # a related attr to the Review model:
    klass.reviews = GenericRelation(Review, related_query_name=klass.__name__) # need to access the name of the class being defined here
    # some other functions are added to a reviewable model here
    return klass

像这样使用装饰器:

# web_store/models.py

from reviews.decorators import related_decorator
from django.db import models

@reviewable
class Product(models.Model):
    name = models.CharField(max_length=50)

@reviewable
class Seller(models.Model):
    name = models.CharField(max_length=50)

然后我希望能够像上面的django文档中描述的那样进行查询:

>>> from reviews.models import Review
>>> Review.objects.filter(product__name__contains='widget')
[<Review: Blue Widget>, <Review: Red Widget>]
>>> Review.objects.filter(seller__name__contains='inc')
[<Review: We Sell Stuff, Inc>, <Review: Cool Stuff, Inc>]

这里的关键是反向查询取决于related_query_name。如何访问装饰器中定义的类的名称? klass.__name__会工作吗?

修改

当我尝试我所描述的内容时,存在一个问题:

>>> Review.objects.filter(product__name__contains='television')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/query.py", line 691, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/query.py", line 709, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1331, in add_q
    clause, require_inner = self._add_q(where_part, self.used_aliases)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1358, in _add_q
    current_negated=current_negated, connector=connector)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1182, in build_filter
    lookups, parts, reffed_aggregate = self.solve_lookup_type(arg)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1120, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1427, in names_to_path
    self.raise_field_error(opts, name)
  File "/Users/atheiman/programming/python/virtualenvs/simple-django/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1433, in raise_field_error
    "Choices are: %s" % (name, ", ".join(available)))
FieldError: Cannot resolve keyword 'product' into field. Choices are: anonymous, comment, comment_approved, content_type, content_type_id, created, id, object_id, score, updated, user, user_id

此外,当我用装饰器添加它时,GenericRelation似乎会中断:

>>> product = Product.objects.all()[0]
>>> product.reviews
<django.contrib.contenttypes.fields.GenericRelation>

当我在模型定义中直接添加reviews关系时,它会创建一个GenericRelatedObjectManager字段,而不是GenericRelation字段:

>>> product = Product.objects.all()[0]
>>> product.reviews
<django.contrib.contenttypes.fields.GenericRelatedObjectManager object at 0x104b06bd0>

1 个答案:

答案 0 :(得分:0)

在我看来,你应该能够通过模型继承和子类中的related_names的特殊情况来完成你想要的东西,但我在这里有一个免责声明:这是基于在非关系上使用这些东西的经验GenericRelations。它似乎应该以下列方式工作,但我可能是错的:

例如,你可以像这样定义一些base(或mixin):

class ReviewBase(models.Model):

    reviews = GenericRelation(Review, related_query_name="%(class)s")

这是来自related_name="%(class)s"的猜测,appears in the docs.

有了这个,您应该能够在任何希望评论能够作为属性的模型中继承此Base模型:

class Product(ReivewBase):
    name = models.CharField(max_length=50)

同样,我不确定是否有效。它基于一种假设,即您可以将此类行为扩展到GenericRelations,但我会尝试一下。