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.
Related
I've been looking for my problem in Django documentation and couldn't find solution.
My problem is that in Api Pannel I cannot insert more objects from "ActorsAndDirectors" class into "cast" Field in "Movie" class. I can only insert one. How to transfrom cast field so I could insert multiple objects from "ActorsAndDirectors" class into "Movie" class
this is the code
`
class ActorsAndDirectors(models.Model):
name = models.CharField(max_length=32, blank=False, null=False)
surname = models.CharField(max_length=32, blank=False, null=False)
role = models.CharField(max_length=11, blank=False, null=False)
def __str__(self):
return f"{self.name} {self.surname}"
class Movie(models.Model):
title = models.CharField(max_length=50, blank=False, null=False, unique=True)
description = models.TextField(max_length=400)
cast = models.ForeignKey(ActorsAndDirectors, on_delete=models.CASCADE)
premiere = models.DateField()
updated = models.DateTimeField()
slug = models.SlugField()
def number_of_ratings(self):
return Rating.objects.filter(movie=self).count()
def avg_rating(self):
score = 0
ratings = Rating.objects.filter(movie=self)
for rating in ratings:
score +=rating.stars
if len(ratings) > 0:
return score/len(ratings)
else:
return 0
def __str__(self):
return f"{self.title}, ({self.premiere})"
`
I looked through Django documentation for some kind of list Field but with no good results. Im looking for help how to transform the field or maybe some other explanation of my problem
What you are looking for is a Many to Many relation. Where many actors and directors can participate in many different movies.
I would like to complement that when querying the database its slower to look for strings. Maybe you should check this choices option for your ActorsAndDirectors role field.
This would help if you try to filter directors or actors later on. Another option would be a Table and a FK.
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.
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 2 tables. User and Group. 1:Many relationship. Each user can only belong to a single group.
here's the model.py.
class Group(models.Model):
group_name = models.CharField(max_length=150, blank=True, null=True)
group_description = models.TextField(blank=True, null=True)
group_creator = models.ForeignKey(User, models.DO_NOTHING)
class User(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
...
group = models.ForeignKey(Group, models.DO_NOTHING)
The issue I have is that they are both referencing each other which is acceptable in MySQL and Oracle, but, I get an error when migrating:
group_creator = models.ForeignKey(User, models.DO_NOTHING)
NameError: name 'User' is not defined
Now when I reverse the order (so, User first than Group), I get
group = models.ForeignKey(Group, models.DO_NOTHING, blank=True, null=True)
NameError: name 'Group' is not defined
This is getting quite frustrating. I have a few work around (make it a many:many and keep creator on Group class), but before I start destroying my datamodel and move data move all the data around, I wonder if anyone has this issue before. How did you solve this? Do you really have to change your datamodel?
as Pourfar mentioned in a comment, you may avoid the NameError via the quoting the model object as string. also it is safe to set related_name for accessing this relation.
class Group(models.Model):
...
group_creator = models.ForeignKey('User', related_name='creator_set')
and then, with your constraint,
Each user can only belong to a single group.
in that case, OneToOneField is more appropriate.
class User(models.Model):
...
group = models.OneToOneField(Group)
then you can access the relations as follows:
# USER is a User object
GROUP_BELONGED = USER.group # access to 1-1 relation
GROUP_CREATED = USER.creator_set.all() # reverse access to foreignkey relation
# now GROUP_BELONGED is a Group object
CREATOR = GROUP_BELONGED.group_creator # access to foreignkey relation
Add related_name to your ForeignKey fields:
class Group(models.Model):
group_name = models.CharField(max_length=150, blank=True, null=True)
group_description = models.TextField(blank=True, null=True)
group_creator = models.ForeignKey('User',related_name='myUser')
class User(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
group = models.ForeignKey('Group', related_name='MyGroup')
I'm trying to create 3 models ; Person, Address and Anniversy. The plan is to have one address and one anniversy for each person. But each address and anniversy can have multiple persons.
So far I have the following, but I think the OneToMany(foreign key) relationships maybe the wrong way round. i.e each address can have one person but each person can have multiple addresses.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def __unicode__(self):
return u'%s' % (self.name)
class Address(models.Model):
person = models.ForeignKey(Person)
address = models.CharField(max_length=150)
def __unicode__(self):
return u'%s' % (self.address)
class Anniversy(models.Model):
person = models.ForeignKey(Person)
anniversy = models.DateField()
def __unicode__(self):
return u'%s' % (self.anniversy)
You create the relationships the other way around; add foreign keys to the Person type to create a Many-to-One relationship:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
anniversary = models.ForeignKey(
"Anniversary", on_delete=models.CASCADE)
address = models.ForeignKey(
"Address", on_delete=models.CASCADE)
class Address(models.Model):
line1 = models.CharField(max_length=150)
line2 = models.CharField(max_length=150)
postalcode = models.CharField(max_length=10)
city = models.CharField(max_length=150)
country = models.CharField(max_length=150)
class Anniversary(models.Model):
date = models.DateField()
I used string names for the other models so they can still be defined after, or you can define the Person model last.
Any one person can only be connected to one address and one anniversary, but addresses and anniversaries can be referenced from multiple Person entries.
Anniversary and Address objects will be given a reverse, backwards relationship too; by default it'll be called person_set but you can configure a different name if you need to. See Following relationships "backward" in the queries documentation.
I would advise, it is slightly better practise to use string model references for ForeignKey relationships if utilising an app based approach to seperation of logical concerns .
So, expanding on Martijn Pieters' answer:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
anniversary = models.ForeignKey(
'app_label.Anniversary', on_delete=models.CASCADE)
address = models.ForeignKey(
'app_label.Address', on_delete=models.CASCADE)
class Address(models.Model):
line1 = models.CharField(max_length=150)
line2 = models.CharField(max_length=150)
postalcode = models.CharField(max_length=10)
city = models.CharField(max_length=150)
country = models.CharField(max_length=150)
class Anniversary(models.Model):
date = models.DateField()