Автогенерируемые Django модели и отношения не отображаются в мета модели
У меня есть следующий код для создания модели Vote для любой данной другой модели, к которой я хочу присоединить это отношение.
from functools import lru_cache
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import gettext_lazy as _
from apps.common.models.base import BaseModel
from apps.common.utils import camel_to_snake
@lru_cache(maxsize=None)
def create_vote_class(klass):
class Vote(BaseModel):
class VoteType(models.IntegerChoices):
UPVOTE = 1, _("Upvote")
DOWNVOTE = -1, _("Downvote")
user = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
related_name=f"{camel_to_snake(klass.__name__)}_votes",
verbose_name=_("User"),
help_text=_("The voter."),
)
post = models.ForeignKey(
klass,
on_delete=models.CASCADE,
related_name=f"{camel_to_snake(klass.__name__)}_votes",
verbose_name=_("Post"),
help_text=_("The post this vote is for."),
)
body = models.IntegerField(
choices=VoteType.choices,
verbose_name=_("Body"),
help_text=_("The actual vote. -1 or +1"),
)
def type(self):
return self.get_body_display()
class Meta:
abstract = True
app_label = klass._meta.app_label # NOQA
klass_name = f"{klass.__name__}Vote"
bases = (Vote,)
class Meta:
app_label = klass._meta.app_label # NOQA
verbose_name = _(klass_name)
verbose_name_plural = _(f"{klass_name}s")
klass_dict = {"__module__": klass.__module__, "Meta": Meta}
vote_class = (type(klass_name, bases, klass_dict) # NOQA
return vote_class
class VoteMaker:
is_relation = True
many_to_many = True
def __init__(self, **kwargs):
self.related_name = kwargs.get("related_name")
self.vote_class_attribute_name = kwargs.get("vote_class_attribute_name")
def contribute_to_class(self, cls, name):
setattr(cls, self.vote_class_attribute_name, create_vote_class(cls))
setattr(cls, name, self.get_votes_field(cls))
def get_votes_field(self, cls):
field = models.ManyToManyField(
get_user_model(),
through=create_vote_class(cls),
related_name=self.related_name or f"voted_{camel_to_snake(cls.__name__)}s",
verbose_name=_("Votes"),
help_text=_("Votes for this post."),
)
return field
def votes(related_name=None, vote_class_attribute_name="vote_class"):
vote_maker = VoteMaker(
related_name=related_name,
vote_class_attribute_name=vote_class_attribute_name,
)
return vote_maker
Я использую его следующим образом:
class Post(BaseModel):
votes = votes()
Генерируется модель Vote, связанные имена и имена атрибутов в модели корректны, я могу запросить модель Vote, там все хорошо.
Но теперь, когда я пытаюсь создать страницу администратора для этой модели Post:
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
fields = ["votes"]
Я получаю ошибку:
Unknown field(s) (votes) specified for Post. Check fields/fieldsets/exclude attributes of class PostAdmin.
Я пытался углубиться в исходный код, но пока добрался только до
model._meta._get_fields(reverse=False)
вызывается, в котором должно быть мое поле m2m, голоса, но его нет.
Кто-нибудь может указать мне правильное направление, как решить эту проблему?
Редактирование 1:
Экстра:
In [1]: from apps.core.models import *
In [2]: Post.votes
Out[2]: <django.db.models.fields.related.ManyToManyField>
In [3]:
Как вы видите, Post.votes действительно является полем ManyToMany
Редактирование 2:
Продвигаясь вперед, я обнаружил, что это не должно выглядеть так, как в редактировании 1 выше.
Например, в модели пользователя:
ipdb> User.groups
<django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x1334e5a90>
Итак, я немного изменил свой первоначальный код, и теперь он выглядит следующим образом:
def contribute_to_class(self, cls, name):
setattr(cls, self.vote_class_attribute_name, create_vote_class(cls))
self.get_votes_field(cls).contribute_to_class(cls, name)
что помогло справиться с некоторыми проблемами.
ipdb> Post._meta.many_to_many
(<django.db.models.fields.related.ManyToManyField: comments>, <django.db.models.fields.related.ManyToManyField: votes>)
Теперь я могу видеть множество полей, но теперь у меня все еще есть некоторые проблемы в других частях django, все еще связанные с администратором.
Полный отслеживание: