Does the 'through' argument in ManyToManyField in Django includes all fields in the related tables? For example will Group contain all Person and Membership fileds? And also how many levels deep can 'through' relationships can be?
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
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)
Yes when you use a through field the associated models' fields are all accessible from the related table. Levels can be deep as you can but it gets complicated better just to create separate tables.
Related
I am getting errors while I am building the following database. Idea is that you create a Team object. Create Student Objects and link them to Teams. And then give the Students points through PointEntry objects. I want to relate the team given to each Student object in each PointEntry Object. But for some reason, Django gives the error:
score_board.Student: (fields.E336) The model is used as an intermediate model by 'score_board.PointEntry.team', but it does not have a foreign key to 'PointEntry' or 'Team'.
Modeladmin Class
class StudentAdmin(admin.ModelAdmin):
list_display = ['name', 'team']
list_filter = ['team']
class PointEntryAdmin(admin.ModelAdmin):
list_display = ['student', 'points']
list_filter = ['student']
Below are the models
class Team(models.Model):
# Team/group model
name = models.CharField(max_length=64)
class Student(models.Model):
# Student model
name = models.CharField(max_length=64)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name}"
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models.OneToOneField(Student, on_delete=models.CASCADE)
points = models.IntegerField()
team = models.ManyToManyField(Team, through='Student')
Your through model needs a ForeignKey to both models. Since the model that defines the ManyToManyField is defined lower, you can not reference to the class. But in Django, you can also use a string literal:
class Student(models.Model):
name = models.CharField(max_length=64)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
pointentry = models.ForeignKey('PointEntry', on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models.OneToOneField(Student, on_delete=models.CASCADE)
points = models.IntegerField()
team = models.ManyToManyField(Team, through='Student')
Django will then replace the string with a reference to the class.
class Team(models.Model):
# Team/group model
name = models.CharField(max_length=64)
student = models. models.ManyToManyField(Student,through='PointEntry ')
class Student(models.Model):
# Student model
name = models.CharField(max_length=64)
team = models. models.ManyToManyField(Team,through='PointEntry')
def __str__(self):
return f"{self.name}"
class PointEntry(models.Model):
# Point entry's made by users appointed to a student in a group
student = models. ForeignKey(Student,on_delete=models.CASCADE)
team = models. ForeignKey(Team, on_delete=models.CASCADE)
points = models.IntegerField()
I think a model like this might work for what you’re trying to achieve.
I am trying to create the proper Django model that could fit the following reqs:
Person Class has 1 to many relations with the Address Class
Person Class has many to many relations with the Group Class
Book Class contains the collections of the Persons and the Groups
This is my code:
class Person(models.Model):
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name+ ' - ' + self.last_name
class Address(models.Model):
person = models.ForeignKey(Person)
address_line = models.CharField(max_length=200)
def __str__(self):
return self.address_line
class Group(models.Model):
group_name = models.CharField(max_length=12)
persons = models.ManyToManyField(Person)
def __str__(self):
return self.group_name
class Book(models.Model):
record_name = models.CharField(max_length=12)
person = models.ForeignKey(Person )
group = models.ForeignKey(Group )
def __str__(self):
return self.record_name
However it's not correct:
1) A Group can now contain multiple Persons but the Persons do not contain any Group.
I am not sure if I should add to the Person class the following code:
groups = models.ManyToManyField(Group)
2) The Book class now contains only 1 record of Person & Group per Book record.
3) When I added the Foreign Keys to the models, I removed
on_delete tag:
person = models.ForeignKey(Person, on_delete=models.CASCADE())
because it does not compile it, asking for some params.
I know how to make all this for C#, but I am a kinda stucked with this simple task in Python/Django.
1) The ManyToMany field should appear only in one of the models, and by looks of things you probably want it in the Person model.
Its important to understand that the data about the ManyToMany field is saved in a differant table. Django only allows this field to be visable through buth models (so basiclly, choose where it is move convinient).
2)By the look of your structure I will suggest you use a ManyToMany field through a different table. here is an example:
class Activity(models.Model):
name = models.CharField(max_length=140)
description = models.TextField(blank=True, null=True)
class Route(models.Model):
title = models.CharField(max_length=140)
description = models.TextField()
activities_meta = models.ManyToManyField(Activity, through = 'RouteOrdering')
class RouteOrdering(models.Model):
route = models.ForeignKey(Route, on_delete=models.CASCADE)
activity = models.ForeignKey(Activity, on_delete=models.CASCADE, related_name='activita')
day = models.IntegerField()
order = models.IntegerField(default=0)
that way the data is binded to the ManyToMany field
I have an existing applicaton where I have to change a regular m2m relationship to a through relation.
I need the ability to add extra parameters to the relation.
My problem is that I need to do it in two places.
This is the standard through example.
class Person(models.Model):
name = models.CharField(max_length=255)
class Group(models.Model):
name = models.CharField(max_length=255)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
I have another model that needs a m2m relation to Membership. My suggestion is the following.
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group, null=True, blank=True)
another_group = models.ForeignKey(AnotherGroup, null=True, blank=True)
class AnotherGroup(models.Model):
name = models.CharField(max_length=255)
members = models.ManyToManyField(Person, through='Membership')
It bugs me that I have to have one field that is null for every instance, or is this my only option?
Another option is using content type in through table.
https://docs.djangoproject.com/en/1.9/ref/contrib/contenttypes/#generic-relations
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Membership(models.Model):
person = models.ForeignKey(Person)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
I have a polling app with one of the models "Choice" consisting of 2 Foreign key fields linked to the "Person" model.
I wanted to automatically populate related "photo_other" field (with the image link) once I have selected the "name" of the person. "name" is also a Foreign Key Field linked with Choice model.
models.py
class Choice(models.Model):
name = models.ForeignKey(Person)
photo_other = models.ForeignKey(Person)
rating = models.DateTimeField(blank=True, null=True)
def __unicode__(self):
return smart_unicode(self.name)
class Person(models.Model):
name = models.CharField(max_length=250)
photo = models.ImageField(upload_to="photos")
pub_date = models.DateTimeField()
def __unicode__(self):
return smart_unicode(self.name)
Why do you want to store the same value in two different tables when they are connected through a foreign key? It just doesn't make sense.
class Choice(models.Model):
name = models.ForeignKey(Person)
rating = models.DateTimeField(blank=True, null=True)
#property
def photo_other(self):
return self.name.photo
class Person(models.Model):
name = models.CharField(max_length=250)
photo = models.ImageField(upload_to="photos")
pub_date = models.DateTimeField()
In order to make photo_other visible under the admin page of Choice model, you can do the following;
class ChoiceAdmin(admin.ModelAdmin):
list_display = ['name', 'rating', 'get_photo']
def get_photo(self, obj):
return obj.photo_other
get_photo.short_description = 'Photo'
I have a simple example of a Member, and a MemberGroup class in a Django app. I would like the following data representation, but I'm not sure if I'm complicating things by using a ManyToMany relationship:
Member:
1 - Member1
2 - Member2
3 - Member3
Group:
1 - Group1
2 - Group2
3 - Group3
MemberGroup:
1 - Member1/Group1
2 - Member1/Group2
3 - Member2/Group3, etc.
I have the following classes:
class Member(models.Model):
nickname = models.CharField(max_length=30, blank=False)
paid = models.BooleanField(default=False)
class MemberGroup(models.Model):
member = models.ManyToManyField(Member)
group_name = models.CharField(max_length=50, db_index=True)
description = models.CharField(max_length=255)
I'd like to be able to use the combo of id/member/group in other model classes (say maybe BlogPost(MemberGroupId, post), but I'm not sure how to get a handle on that particular object. The current model generates the correct database tables, I'm just not sure how to get a handle on an object that represents the link table. In the Django shell, when I get MemberGroup(id=1) and try printing the member associated with that MemberGroup, I get
<django.db.models.fields.related.ManyRelatedManager object at 0x2ae6c10>
Do I need to create another class, Group, and then have Member and Group be foreign keys in the MemberGroup class to accomplish what I need or can I use my current setup? Thanks for any help!
First, I would do the models like this:
from django.db import models
class Group(models.Model):
group_name = models.CharField(max_length=50, db_index=True)
description = models.CharField(max_length=255)
class Member(models.Model):
nickname = models.CharField(max_length=30, blank=False)
paid = models.BooleanField(default=False)
groups = models.ManyToManyField(Group)
Then you could do this:
>>> test_group = Group(group_name='Admin', description='Admin group')
>>> test_group.save()
>>> m1 = Member(nickname='Robert')
>>> m1.save()
>>> m1.groups.add(test_group)
>>> m1.save()
>>> m1.groups.all()
[<Group: Group object>]
>>> test_group.objects.get(group_name='Admin').member_set.all()
[<Member: Member object>]
That code creates a group "Admin", then a member "Robert". It associates the "Robert" user to the "Admin" group. I can then return all members of the "Admin" group using member_set.
EDIT: There's nothing exactly wrong with putting the ManyToMany field in the Group model to refer to the members, but it seems (to me, anyway) more logical to associate a member to a group rather than a group to a member.
So reading the docs again related to the many_to_many relationship yielded the correct solution for me, which was using this "through" designation on a model. My models now look like this:
class Member(models.Model):
nickname = models.CharField(max_length=30, blank=False)
paid = models.BooleanField(default=False)
class Group(models.Model):
group_name = models.CharField(max_length=50, db_index=True)
description = models.CharField(max_length=255)
created_by = models.ForeignKey('Member', related_name='created_by')
created_date = models.DateTimeField(auto_now=True)
members = models.ManyToManyField(Member, through="Membership")
def __unicode__(self):
return self.group_name
class Membership(models.Model):
member = models.ForeignKey(Member)
group = models.ForeignKey(Group)
active = models.BooleanField(default=True, db_index=True)
join_date = models.DateTimeField(auto_now=True)
league_manager = models.BooleanField(default=False, db_index=True)
def __unicode__(self):
return "%s (%s)" % (self.member, self.group)
and now I can get a handle on the Membership class where as before, the relationship was created, but there was no explicit object for me to use. Thanks everyone.