Access fields in Django intermediate model - django

I'm creating a Person Group and Membership as described in Django docs for intermediate model.
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
It is possible to access the Person from a Group object with:
>>>Group.members.name
Does Django creates another query to fetch the Person?
Can I access the date_joined field from a Group object?
The thing that confuses me is that I would expect to get the Person name field with:
>>>Group.members.person.name
What happens if a Person has a field 'name' and also the intermediate model have a field 'name'.

The members field in your example is a ManyToManyField, so it's a way to access many people rather than one person.
The object that is under the members field is actually a special type of Manager, not a Person:
>>> print my_group.members
<django.db.models.fields.related.ManyRelatedManager object at 0x181f7d0>
To understand better what a Manager is, see the documentation.
To access a person's name you would do for example:
>>> for person in my_group.members.all():
>>> print person.name
You cannot access the fields in your Membership model via the Manager in the members field. To access any of the fields in it you would do:
>>> for membership in my_group.membership_set.all():
>>> print membership.date_joined
And so if you had a field called name in your Membership model, you would access it like this:
>>> for membership in my_group.membership_set.all():
>>> print membership.name
A second way to access a Person's name would be:
>>> for membership in my_group.membership_set.all():
>>> print membership.person.name
Note that membership_set is a default name for the manager pointing towards the membership, but it can be changed by specifying related_name in the corresponding foreign key. For example if the foreign key from the Membership to the Group would be defined like such:
group = models.ForeignKey(Group, related_name="group_members")
Then you would access the manager using group_members:
>>> for membership in my_group.group_members.all():
>>> print membership.name
Hope that helps a little :)

Use the manager of the membership class:
MyGroup.membership_set.all()
instead of:
MyGroup.members.all()

Related

Django two-way relationship between group and group members

I am trying to model a two-way relationship between group members and the group they belong to.
More specifically, I'd like to model something like this:
class Member(models.Model):
name = models.CharField(max_length=50)
my_group = models.ForeignKey('Group')
#leader = models.BooleanField()
class Group(models.Model):
name = models.CharField(max_length=50)
leader = models.ForeignKey('Member')
So I want every member to be linked to a group. But each group should have a leader. I'd normally add a "leader" attribute to assign a leader for a group or add an extra model / table. However, I would like to be able to choose the leader from the members list for a specific group in the Django Admin interface and the above attempt results in an error of course, because the two models reference each other.
Here's the error:
ERRORS: app1.Group.leader: (fields.E303) Reverse query name for 'Group.leader' clashes with field name 'Member.my_group'. HINT: Rename field 'Member.my_group', or add/change a related_name argument to the definition for field 'Group.leader'
What's the best way to achieve this?
You can have intermediate model called Membership to define the level of membership
models:
class Member(models.Model):
name = models.CharField(max_length=50)
# may be a relations to User here
class Group(models.Model):
name = models.CharField(max_length=50)
members = models.ManyToManyField(Member, through=Membership)
class Membership(models.Model):
group = models.ForeignKey(Group)
members = models.ForeignKey(Member)
# member level
is_leader = models.BooleanField(default=False)
you can check the documention on ManyToMany fields for some more examples.

how to query all objects in one set that do not have a foreign key in another object in django

I have the following objects:
class Customer(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
customer = models.ForeignKey(Customer)
class GroupMember(models.Model):
group = models.ForeignKey(Group)
member = models.ForeignKey(Member)
class Member(models.Model):
customer = models.ForeignKey(Customer)
I can get all the members assigned to a group with this:
group_members = group.groupmember_set.all()
All of the members available for a certain customer with this:
member_list = customer.members.all()
I want to create a list of all of members available for a customer that are not assigned to a group. Something like:
not_group_members = Member.objects.filter(?)
How can I create this query?
With your current setup, that's not possible since Member has a non-nullable foreign key to group.
However, if you change to this:
class Member(models.Model):
customer = models.ForeignKey(Customer, null=True)
Then you can find Members that are not in any group.
not_group_members = Member.objects.filter(group=None)
customer.members.filter(groupmember_set__isnull=True)
(I used members here because you did, even though the default related name would be member_set.)
I was able to accomplish this with:
customer.members.filter(groupmember__isnull=True)

django error while updating only one field in model

I want to update only one field in my model. However, I am getting an error.
This is my model:
class People(models.Model):
name = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
class Salary(models.Model):
id_of_people=models.ForeignKey(People)
salary = models.IntegerField(required=False)
In views.py
-When I try this one to update :
def update(request):
a=Salary.objects.get(id_of_people_id=1)
a.salary=500
Salary().save()
My Error says:
IntegrityError at/update
salary.id_of_people_id may not be NULL
and traceback indicates:
Salary().save()
-When I try this one :
def update(request):
a=Salary.objects.get(id_of_people_id=1)
a.salary=500
Salary().save(save_fields=['salary'])
-I get this error:
save() got an unexpected keyword argument 'save_fields'
Can you please help me to update only one field in my table ?
In both of those cases you'll want to call save on the model instance you've created, not the model class--that is, you should be saving a, not Salary:
a.salary=500
a.save()
When you do Salary().save(), what's happening is that you create a brand new, empty model instance, and then try to commit that to the database, rather than committing the one that you had just modified.
If a ForeignKey is defined in your model, the contraint will be enforced at the db level so you will need to save the object reference by the Foreign key before you save the referring object.
You may also want to reconsider whether or not the foreign keys should be defined in person or Salary.
If you were to define the model like this:
class Person(models.Model):
name = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
salary = models.ForeignKey(Salary)
class Salary(models.Model):
amount = models.IntegerField(required=False)
Then you could define your views function so that it looks like this:
def update(request):
s = Salary(amount=request.POST['salary'])
s.save()
p = Person(name=request.POST['name'], lastname=request.POST['lastname'], salary=s)
p.save()
The nice about this is that you could then reference the salary from a Person instance:
Person.objects.get(pk=1).salary.amount
I can't help but ask the question though why you really need these in separate objects. Things might be simpler if your model looked like this:
class Person(models.Model):
name = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
salary = models.IntegerField(required=False)

Difference between ForeignKey(User, unique=True) and OneToOneField [duplicate]

This question already has answers here:
What's the difference between django OneToOneField and ForeignKey?
(11 answers)
Closed 8 years ago.
What is different between models.ForeignKey(Modelname, unique=True) and models.OneToOneField in Django?
Where should I use models.OneToOneField and models.ForeignKey(Modelname, unique=True)?
A OneToOneField is very similar to a ForeignKey with unique=True. Unless you are doing multiple table inheritance, in which case you have to use OneToOneField, the only real difference is the api for accessing related objects.
In the Django docs it says:
Conceptually, this is similar to a ForeignKey with unique=True, but the "reverse" side of the relation will directly return a single object.
Let's show what that means with an example. Consider two models, Person and Address. We'll assume each person has a unique address.
class Person(models.Model):
name = models.CharField(max_length=50)
address = models.ForeignKey('Address', unique=True)
class Address(models.Model):
street = models.CharField(max_length=50)
If you start with a person, you can access the address easily:
address = person.address
However if you start with an address, you have to go via the person_set manager to get the person.
person = address.person_set.get() # may raise Person.DoesNotExist
Now let's replace the ForeignKey with a OneToOneField.
class Person(models.Model):
name = models.CharField(max_length=50)
address = models.OneToOneField('Address')
class Address(models.Model):
street = models.CharField(max_length=50)
If you start with a person, you can access the address in the same way:
address = person.address
And now, we can access the person from the address more easily.
person = address.person # may raise Person.DoesNotExist
When you access a OneToOneField you get the value of the field you queried. In this example a book model's 'title' field is a OneToOneField:
>>> from mysite.books.models import Book
>>> b = Book.objects.get(id=50)
>>> b.title
u'The Django Book'
When you access a ForeignKey you get the related model object, which you can then preform further queries against. In this example the same book model's 'publisher' field is a ForeignKey (correlating to the Publisher class model definition):
>>> b = Book.objects.get(id=50)
>>> b.publisher
<Publisher: Apress Publishing>
>>> b.publisher.website
u'http://www.apress.com/'
With ForeignKey fields queries work the other way too, but they're slightly different due to the non-symmetrical nature of the relationship.
>>> p = Publisher.objects.get(name='Apress Publishing')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive Into Python>, ...]
Behind the scenes, book_set is just a QuerySet and can be filtered and sliced like any other QuerySet. The attribute name book_set is generated by appending the lower case model name to _set.
I hope this helps illustrate the differences between the relationships created.

How do I delete an instance of an intermediate model in a Django Many-to-many relationship?

According to an example, I have three models:
class User(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(User, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(User)
group = models.ForeignKey(Group)
date_joined = models.DateField()
Adding members works. But how do I delete a single Membership instance
(a User quits a group), without deleting neither the User, nor the Group?
When I try deleting it like this:
u = User(request.user)
g = Group.objects.get(id=group_id, membership__user=u)
m = Membership(user=request.user, group=g)
m.delete()
I get an error:
AssertionError at /groups/quit/1/
Membership object can't be deleted because its id attribute is set to
None.
In the line
m = Membership(user=request.user, group=g)
You created a new Membership you didn't fetch one from the database. That is why its id attribute is set to None.
Perhaps you meant
m = Membership.objects.get(user=request.user, group=g)
This particular error is triggered by the fact, that your m instance of class Membership is unsaved, so its primary key is None. Apparently, it is impossible to delete such an unsaved instance (which makes sense, because there is nothing to "delete").