Общее взаимно-однозначное отношение в Django

Мне нужно установить отношение один к одному, которое также должно быть общим. Может быть, вы можете посоветовать мне лучший дизайн. Пока я пришел к следующим моделям

class Event(models.Model):
    # skip event related fields...
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id')

class Action1(models.Model):
    # skip action1 related fields...
    events = generic.GenericRelation(Event, content_type_field='content_type', object_id_field='object_id')

    @property
    def event(self):
        return self.events.get() # <<<<<< Is this reasonable?

class Action2(models.Model):...

В Django Admin в списке событий я хочу собрать все действия, а оттуда я хочу перейти на страницы администратора для действий. Можно ли избежать создания свойства event в моделях действий? Есть ли лучшее решение? Было бы неплохо объединить поле events и свойство event в одном определении. Проект, над которым я работаю, использует Django 1.1.


person Andrei    schedule 20.10.2011    source источник
comment
Если вы действительно хотите избежать events, вам нужно выполнить запрос к таблице Event вручную, как это предлагается ниже. Я бы все же предпочел иметь events в качестве GenericRelation, а затем использовать self.events.first() в свойстве event. Кроме того, это позволит вам легче удалить уникальное ограничение в будущем.   -  person alexcasalboni    schedule 13.07.2017


Ответы (2)


Недавно я наткнулся на эта проблема. То, что вы сделали, прекрасно, но вы можете немного обобщить это, создав миксин, который прозрачно меняет отношения на противоположные:

class Event(models.Model):
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id')

class EventMixin(object):
     @property
     def get_event(self):
         ctype = ContentType.objects.get_for_model(self.__class__)
         try:
             event = Event.objects.get(content_type__pk = ctype.id, object_id=self.id)
         except:
            return None 
         return event

class Action1(EventMixin, models.Model):
    # Don't need to mess up the models fields (make sure the mixing it placed before models.Model)
    ...

и

action = Action1.object.get(id=1)
event = action.get_event

Возможно, вы захотите добавить кэширование и в обратное отношение.

person Timmy O'Mahony    schedule 15.02.2012
comment
Работает хорошо, но не дает возможности: Action.objects.select_related('event') - person ramusus; 24.05.2013
comment
Мне не нравится это решение -› GenericRelation выглядит намного лучше, даже если вам нужно использовать self.events.get() или self.events.first() - person alexcasalboni; 13.07.2017
comment
То же, что комментарии выше. То, что у вас было изначально с GenericRelation, намного лучше. - person s-block; 27.02.2018

Используйте .get() вернуть повышение, если объект не существует, .first() вернуть None, если объект не существует.

Имя events_relation — это элегантный способ отличить event от events.

class Event(models.Model):
    content_type      = models.ForeignKey(ContentType)
    object_id         = models.PositiveIntegerField()
    content_object    = generic.GenericForeignKey('content_type', 'object_id')

    class Meta:
        unique_together   = ('content_type', 'object_id') # Important

class Action1(models.Model):
    events_relation = generic.GenericRelation(Event)

    @property
    def event(self):
        # Return the object in exists
        # else None 
        return self.events_relation.first()
person Geek Actualizado    schedule 21.02.2020