Use fields between OneToOneField in a model - django

I am in a situation where I cannot use inheritance. Say I have two models, a Person and a Passport, for this example assume they have a One-to-one relationship:
class Passport(models.Model):
full_name = models.CharField()
class Person(models.Model):
passport = models.OneToOneField(Passport)
Now from here, I want to get a Person's full_name. In my application, a Person cannot exist without a unique Passport, so how do I access the full_name attribute through a Person object. I ultimately want to be able to do something like this:
class Person(models.Model):
passport = models.OneToOneField(Passport)
def __unicode__(self):
return self.passport.full_name
Is there a simple solution to this is Django?

Yes, the simple and correct way of doing this is exactly what you did: self.passport.full_name returns the full name of the person, which is an attribute of the passport.
Notice that because the passport ForeignKey cannot be null as you defined it, the self.passport will always exist.

Related

Authentication of different types of user of same name in django?

I have a model of teacher and student . If I created a Student and again create a teacher with same name and email and stuffs, It gets created .. Can't be a teacher and student of same people in a college. How to apply in rest api?
There are several way to solve this, depending on your requirements and preference.
One way would be at database level with constraints. In this case you'd have to create model with all people regardless of their status and add additional boolean field that differentiates between Students and Teachers. This would look like this:
class People(models.Model):
email = models.EmailField(unique=True)
is_teacher = models.BooleanField()
unique means that email must be unique throughout the table and if you try to add another person with the same email, IntegrityError will be raised.
There is also a possibility to use unique over several fields with unique_together like this.
class People(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
class Meta:
unique_together = (('first_name', 'last_name'),)
This means that first_name and last_name together must be unique.
Another approach would be to check if student or teacher is already in one or the other table before saving with filter() and exists().
teacher_exists = Teacher.objects.filter(teacher=something).exists()
student_exists = Teacher.objects.filter(student=something).exists()
exists() returns True if the QuerySet contains any results, and False if not.

Trouble overriding save method on Django model with ManyToManyField

I'm having trouble overriding the save method on a Django model to check a restriction on a many-to-many field.
Say I have the following models:
class Person(models.Model):
name = models.CharField()
class ClothingItem(models.Model):
description = models.CharField()
owner = models.ForeignKey(Person)
class Outfit(models.Model):
name = models.CharField()
owner = models.ForeignKey(Person)
clothing_items = models.ManyToManyField(ClothingItem)
I would like to put a restriction on the save method of Outfit that ensures that each ClothingItem in a given outfit has the same owner as the Outfit itself.
I.e. I'd like to write:
class Outfit(models.Model):
name = models.CharField()
owner = models.ForeignKey(Person)
clothing_items = models.ManyToManyField(ClothingItem)
def save(self, *args, **kwargs):
for ci in self.clothing_items:
if ci.owner != self.owner:
raise ValueError('You can only put your own items in an outfit!)
super(Outfit, self).save(*args, **kwargs)
but when I try that I get an error about <Outfit: SundayBest>" needs to have a value for field "outfit" before this many-to-many relationship can be used.
Any ideas what's going wrong here?
There are two issues going on here. To directly answer your question, the error basically means: You cannot refer to any m2m relationship if the original object(an instance of Outfit here) is not saved in database.
Sounds like you are trying to do the validation in save() method, which is a pretty bad practice in django. The verification process should typically happen in Form that creates Outfit objects. To override default django form, please refer to django ModelAdmin.form. To understand how to do validation on django forms, check ModelForm validation.
If you want code to refer to for m2m validation, I found a good example from SO.

Django REST Framework nested relations without related names

I have some models that relates to User, but does not have a related name on user:
class Registration(models.Model):
user = models.OneToOneField('auth.User', related_name='+')
class ManyToOneModel(models.Model):
user = models.ForeignKey('auth.User', related_name='+')
I would like to make a serializer for User, which can have this as a nested resource. Is there a way to specify what the queryset/object is? This is an example of what I have - and it completly expectedly failes with 'User' object has no attribute 'registration':
class UserSerializer(serializers.Serializer):
pk = serializers.Field()
registration = RegistrationSerializer()
many_to_one_model = ManyToOneModelSerializer(many=True, required=False)
I guess you'd need to manually query for the related objects and then construct the serializers by hand. You'd then construct the final representation and pass that as the data parameter to a Response object.
It seems like you're making life difficult though. If you just define the related_name on your related models you could use ModelSerializer (or HyperlinkedModelSerializer) and it would all Just Work™. — Is there some reason why you can't do this?

Many to many association

I don't understand why we have to use ManyToManyField to declare a many to many association. To do so, I would create another table with two foreign keys, period!
Here is an example from the doc https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
extraField = models.DateField()
I would just write:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
extraField = models.DateField()
Is it correct? What is the difference?
There is nothing wrong with defining an intermediate model for the relationship. That way you can store extra information on the intermediate model (Membership), like when the person joined the group, or if the membership is still valid. However, most of the time you don't need the extra information and only want to store which person is member of which group. In those cases, you could just use a simple ManyToManyField. Django makes it very easy to work with those fields, so you can do group.members.add(user) and group.members.delete(user), compare that to:
Membership.objects.create(user=user, group=group)
Membership.objects.get(user=user, group=group).delete()
Disclaimer: pseudo-code, might not actually work
You can still use ManyToManyField with an intermediate model, this allows for most of the Django ManyToMany conveniences, but with some restrictions:
Unlike normal many-to-many fields, you can’t use add, create, or assignment (i.e., beatles.members = [...]) to create relationships.
Why? You can’t just create a relationship between a Person and a Group - you need to specify all the detail for the relationship required by the Membership model. The simple add, create and assignment calls don’t provide a way to specify this extra detail. As a result, they are disabled for many-to-many relationships that use an intermediate model. The only way to create this type of relationship is to create instances of the intermediate model.
The remove() method is disabled for similar reasons. However, the clear() method can be used to remove all many-to-many relationships for an instance.
Once you have established the many-to-many relationships by creating instances of your intermediate model, you can issue queries. Just as with normal many-to-many relationships, you can query using the attributes of the many-to-many-related model.
Source: docs.djangoproject.com

Should I use a seperate table instead of many to many field in Django

I needed to assign one or more categories to a list of submissions, I initially used a table with two foreign keys to accomplish this until I realized Django has a many-to-many field, however following the documentation I haven't been able to duplicate what I did with original table.
My question is : Is there a benefit to using many-to-many field instead of manually creating a relationship table? If better, are there any example on submitting and retrieving many-to-many fields with Django?
From the Django docs on Many-to-Many relationships:
When you're only dealing with simple many-to-many relationships such
as mixing and matching pizzas and toppings, a standard ManyToManyField
is all you need. However, sometimes you may need to associate data
with the relationship between two models.
In short: If you have a simple relationship a Many-To_Many field is better (creates and manages the extra table for you). If you need multiple extra details then create your own model with foreign keys. So it really depends on the situation.
Update :- Examples as requested:
From the docs:
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)
You can see through this example that membership details (date_joined and invite_reason) are kept in addition to the many-to-many relationship.
However on a simplified example from the docs:
class Topping(models.Model):
ingredient = models.CharField(max_length=128)
class Pizza(models.Model):
name = models.CharField(max_length=128)
toppings = models.ManyToManyField(Topping)
There seems no need for any extra data and hence no extra model.
Update 2 :-
An example of how to remove the relationship.
In the first example i gave you have this extra model Membership you just delete the relationship and its details like a normal model.
for membership in Membership.objects.filter(person__pk=1)
membership.delete()
Viola! easy as pie.
For the second example you need to use .remove() (or .clear() to remove all):
apple = Toppings.objects.get(pk=4)
super_pizza = Pizza.objects.get(pk=12)
super_pizza.toppings.remove(apple)
super_pizza.save()
And that one is done too!