I don't understand why should I use ManyToManyField if I will (or might) store extra information along the many-to-many relationships.
The doc here shows this example:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
Why even having the members field there, if the developer already defines the relationship model and also defines the ForeignKeys? :D Why not leaving out the members ManyToManyField ?? What is the benefit? Thank you.
I don't understand why should I use ManyToManyField if I will (or might) store extra information along the many-to-many relationships.
You do not need to do this. But it makes querying more effective. Indeed, you can now query with:
Group.objects.filter(members=some_person)
this is shorter and more descriptive than working with:
Group.objects.filter(membership__person=some_person)
It is thus a "coding shortcut" to go from the Group model to the Person model and vice versa.
Hello i am trying to understand Django Data Models and it´s possibilities. After using one-to-one and m2m i am now trying to understand the m2m extra fields. So i followed the Example of the Django Doc and populated the models with some Data.
models.py
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
So far everything is fine, but as i looked into the model tables, via pgAdmin 4 ( i am using PostgreSQL), i could not find any members column in the Group model. So i made a little visual of my model and some screenshots of pgAdmin, for better understanding.
As you can see Group has only a id and name column.
Question
my question: Is this m2m members relation of Group, only some kind of "virtual" created relation via the Membership.person.fk and Membership.group.fk? Meaning there is no field which will be populated? I try to imagine that it works like a recursive, instance query?
I have a couple of models that have need of a common set of fields. It is basically a set of various different types of contacts:
# models I would like to share
class Address(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
street1 = models.CharField(_('street1'), max_length=125, blank=True)
street2 = models.CharField(_('street2'), max_length=125, blank=True)
city = models.CharField(_('city'), max_length=50, blank=True)
state = models.CharField(_('state'), max_length=2, blank=True)
zip_code = models.CharField(_('zip_code'), max_length=10, blank=True)
class Phone(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
phone = models.CharField(_('phone'), max_length=50, blank=True)
# these are the models that I would like to have addresses and phone numbers
class Contact(models.Model):
name = models.CharField(_('name'), max_length=50, blank=False)
class Organization(models.Model):
name = models.CharField(_('name'), max_length=255, blank=False)
email = models.CharField(_('email'), max_length=100, blank=True)
website = models.CharField(_('website'), max_length=255, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.CharField(_('photo'), max_length=255, blank=True)
I would like to share the Phone and Address models with the Contact, Organization and UserProfile models. My first attempt was to add ForeignKey on each of Contact, Organization and UserProfile but after more research I believe this is backwards, so I moved the ForeignKey to Address and Phone but then discovered that ForeignKey can belong to one and only one model. In addition to sharing this data structure between multiple different contact types, I would like the ability to add more than one address or phone number to a contact. A contact could have a home address, work address, mobile number, and work number for example. So I have basically 2 questions:
1) Is sharing a model in this way a reasonable thing to do?
2) How would I go about setting up the models?
If I've understood correctly; you want your Contact, Organization, and UserProfile models to all have fields for Address and Phone. Moreover, they can each have more than one address/phone.
Is this reasonable? Sounds so to me.
How could you go about setting up the models? Generic Relations spring to my mind.
Consider just Contact and Address for now. Your second attempt is correct: we have a Many-to-one relationship (one contact, many addresses), so we need to make Contact a ForeignKey field in the Address model, like so:
class Address(models.Model):
<other_fields>
contact = models.ForeignKey('Contact', on_delete=models.CASCADE)
This allows you to assign multiple addresses to each contact.
Moving on, you essentially want your Address model to have multiple foreign keys: Contact, Organization, and UserProfile. One way of achieving this is to use Generic Relations. This makes use of Django's built-in "contenttypes" framework, and allows you to create GenericForeignKey fields that point to more than one model. I encourage you to read the docs linked, since generic relations aren't so trivial. In the end, you'll have something like:
class Address(models.Model):
label = models.CharField(max_length=50, blank=True)
street_1 = models.CharField(max_length=125, blank=True)
street_2 = models.CharField(max_length=125, blank=True)
etc...
models_with_address = models.Q(app_label='app_name', model='contact') | \
models.Q(app_label='app_name', model='organization') | \
models.Q(app_label='app_name', model='userprofile')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=models_with_address)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
With this, you can create several addresses for each of the models specified in the models_with_address query. To be able to query the addresses for a given contact/organization/etc., you'll need a reverse generic relation. Setting this up involves adding the line address = GenericRelation(Address) to the respective models.
For further generalisation, you could create a ContactableModel (or whatever) class:
class ContactableModel(models.Model):
address = GenericRelation('Address')
phone = GenericRelation('Phone')
Any model with an address and phone number (Contact, Organization, etc.) could then inherit this so that you don't have to repeatedly include those two fields. You could also improve the models_with_address limit, so that we have something like limit_choices_to=<subclasses_of_ContactableModel>.
Hope this helps!
A simple solution would be to add ManyToManyField(Address|Phone) (i.e. two M2M fields)
to Contact, Organization and UserProfile.
But that would mean different contacts/organizations/users could share addresses.
While this looks tempting, the problem with such a solution is that if someone
edits an address of a contact, they also change the address for all the remaining objects in the system!
Another possible solution which avoids the above problem
and doesn't require M2M fields or generic relationships would be to use
multi-table inheritance:
class Address(models.Model):
entity = models.ForeignKey(Entity)
...
class Entity(models.Model):
pass
class Contact(Entity):
...
class Organization(Entity):
...
(Under the hood Django creates implicit OneToOneField-s
that point from Contact & Organization to Entity.)
I have two models in my django application, event/user, that have a many-to-many relationship.
Event can have many attendees(user).
User can attend many events.
Django automatically made a bridge table for for this relationship.
My question is how can I add new fields to this table using migrations as there is not a model for this Bridge table.
This can be done by creating a "through" table and adding the fields on it. Check out the docs at https://docs.djangoproject.com/es/1.10/topics/db/models/
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
This bridge table is called association table. Imo it is bad practice to edit this table that way. Connecting models by many to many field suggest that there is no model between. If your association table is supposed to represent additional data then you should create new model
class NewModel(models.Model):
attender = models.ForeignKey(User, null=False)
event = models.ForeignKey(Event, null=False)
# additional fileds
and remove many to many relationship from event
I have a Book model that stores the authors as a ManyToManyField to Person through table Author. The intermediate Author table exists because it adds the order property:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ManyToManyField(Person, through='Author')
class Author(models.Model):
book = models.ForeignKey(Book)
person = models.ForeignKey(Person)
order = models.PositiveSmallIntegerField()
What I want is to verify that, for a given book, order starts with 1 and is contiguous. Where can I do this check? I can't do it in Book.save(), because first you need to save the book, then you add authors to it (that's how ManyToManyFields work AFAIU). I can't do it in Author.save(), because it is normal that there is temporary loss of integrity (e.g. if there are 4 authors, I delete author with order=3, then I update the last author setting order=3; there is loss of integrity between the two steps).
I guess the integrity should be checked on commit. Is there any standard or recommended practice for doing this?