Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic one-to-one relation in Django

I need to set up one-to-one relation which must also be generic. May be you can advice me a better design. So far I came up to the following models

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):... 

In Django Admin in event list I want to collect all actions, and from there I want go to admin pages for actions. Is it possible to avoid creating event property in the action models? Is there a better solution? It would be nice to combine the field events and the property event in a single definition. The project I am working with uses Django 1.1

like image 667
Andrei Avatar asked Oct 20 '11 14:10

Andrei


People also ask

What is generic relationship in Django?

Generic relations. Adding a foreign key from one of your own models to ContentType allows your model to effectively tie itself to another model class, as in the example of the Permission model above.

What is Django one to one field?

One-to-one fields:This is used when one record of a model A is related to exactly one record of another model B. This field can be useful as a primary key of an object if that object extends another object in some way. For example – a model Car has one-to-one relationship with a model Vehicle, i.e. a car is a vehicle.

How do you represent one to many relationship in Django?

To handle One-To-Many relationships in Django you need to use ForeignKey . The current structure in your example allows each Dude to have one number, and each number to belong to multiple Dudes (same with Business).

What are Django Contenttypes?

Content types are Django's way of identifying database tables. Every Database table is represented as a row in the content type table which is created and maintained by Django.


2 Answers

I recently came across this problem. What you have done is fine, but you can generalise it a little bit more by creating a mixin that reverses the relationship transparently:

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)     ... 

and

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

You might want to add caching to the reverse relationship too

like image 142
Timmy O'Mahony Avatar answered Oct 07 '22 16:10

Timmy O'Mahony


Using the name events_relation for the GenericRelation inside the 'Action1' class is an elegant way to clearly differentiate event from events.

Inside Action1.event I recommend using .first() due to it returning None if the object doesn't exist. .get() raises an exception if the object doesn't exist and this behavior can be unwanted.

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() 
like image 42
Leonardo Ramírez Avatar answered Oct 07 '22 14:10

Leonardo Ramírez