How do I save two models related with OneToOne in Django? - django

Here is my code:
from django.db import models
from django.contrib.auth.models import User
class Person(models.Model):
user = models.OneToOneField(User, primary_key=True)
title = models.CharField(max_length=3, choices=PERSON_TITLE_CHOICES)
first_name = models.CharField(max_length=100)
Basically, what I want to do, is when I am registering a new user, I'd like to save a Person model, already with the relation to the User.

You have two options here. You can either register a post-save hook for User and create your Person there, or you can create them together. I do the latter. Just make sure you wrap them in a transaction so if one fails, the other does also:
with django.db.transaction.commit_on_success():
user = User(...)
user.save()
person = Person(user = user, ...)
person.save()

Related

Django makemigrations error on many to many relationship

What i want:
Store information about running of group of people.
What i did:
from django.db import models
from django.contrib.auth.models import User
from datetime import timedelta
class Route(models.Model):
name = models.CharField(max_length=50)
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(User, through='RunnerResult', through_fields=["user", "run"])
class RunnerResult(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
run = models.ForeignKey('Run', on_delete=models.PROTECT)
result = models.DurationField(default=timedelta())
Problem:
When i do makemigrations i have the following error:
SystemCheckError: System check identified some issues:
ERRORS:
run.Run.runners: (fields.E339) 'RunnerResult.run' is not a foreign key to 'User'.
HINT: Did you mean one of the following foreign keys to 'User': user?
run.Run.runners: (fields.E339) 'RunnerResult.user' is not a foreign key to 'Run'.
HINT: Did you mean one of the following foreign keys to 'Run': run?
Tried to swap through_fields and models between each other and some other actions. I'm starting to think of my misunderstanding of M2M relationship.
You specified the through_fields in the wrong order. You first should specify the relation that refers to the source, and then then one to the target, so:
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(
User,
through='RunnerResult',
through_fields=('run', 'user')
)
Since there is however only one ForeignKey to Run, and one to User, you do not need to specify the through_fields=… parameter [Django-doc]. So you can implement this as:
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(
User,
through='RunnerResult'
# no through_fields
)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django order queryset by OnetoOneField

I have a two models UserProfile and User. The UserProfile model has a onetoone filed with the user. I figured out how to order the UserProfile by a variable it contains. However, I do not know how to order the items in UserProfile by their related User models
class User(AbstractBaseUser):
full_name = models.CharField(max_length=255, blank=True, null=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
lunch_price = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
I can run this query just fine:
from myapp.models import UserProfiles
print(UserProfile.objects.all().order_by('lunch_price')
I thought i'd run something like this to order by full_name, but it doesn't work
from myapp.models import UserProfiles
print(UserProfile.objects.all().order_by('user.full_name')
How do I make that jump to the user model?
You can follow OneToOneFields and other foreign key relations by using a double underscore (__):
UserProfile.objects.all().order_by('user__full_name')
This acts a bit similar to how in Python one usually obtains (chains of) attributes. For example if the User has a OneToOneField to (for example) an Office model, then we can for instance query with user__office__floor to sort the users by the floor where their office is located.
Mind that this only works given we are working with fields. So if you would for instance have a User class with a first_name and a last_name, and you use a #property for the full_name (in other words, the full_name is determined when needed), then this will not work, and you will have to sort at Python level. This is logical, since the database of course does not know anything about the Django ORM layer and hence it can not interpret what this property is doing.

Django models: Parent model accessing child model field

I have the following models
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
class Tutor(UserProfile):
# tutor_fields here
From User object how can I access Tutor? is it with user.profile?
user.profile.select_related('tutor') does not seem to work.
OneToOneField work like ForeignKey except unique=True and you don't need to specify the related_name (you can specify if you want to change it).
For you example:
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
class Tutor(UserProfile):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='tutor')
details = models.CharField(max_length=100)
NB: Use a ForeignKey if you want/need one Tutor for many UserProfile, else if you want one (and only one) Tutor for one UserProfile
And you can access to the Tutor bu UserProfile by UserProfile.tutor.details.
As described in the django docs, you should be able to access it with user.user_profile.

Django: User Profile in 1.9

RELATED: get user profile in django
The above shows how to get user profile but i read that the accepted answer method is deprecated.
How would I create/get/use user profile in django 1.9?
models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
address = models.TextField()
......
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
The above code will create a UserProfile record whenever a new user is created on User table. Then you can access the profile details like,
address = request.user.profile.address
get_profile() method returned additional informations about User. Currently, these informations can be stored in Custom User Model or in a seperate model which is related to User Model. You can do that by simply adding one2one relation with User model to your custom User model, or by subclassing the AbstructUserBase model.
Subclassing User Model example:
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
...
One2One Relation with User model example:
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)

Django models with OneToOne relationships?

Let's say I'm using the default auth.models.User plus my custom Profile and Address models which look like this:
class Profile(models.Model):
user = models.OneToOneField(User)
primary_phone = models.CharField(max_length=20)
address = models.ForeignKey("Address")
class Address(models.Model):
country = CountryField(default='CA')
province = CAProvinceField(default='BC')
city = models.CharField(max_length=80)
postal_code = models.CharField(max_length=6)
street1 = models.CharField(max_length=80)
street2 = models.CharField(max_length=80, blank=True, null=True)
street3 = models.CharField(max_length=80, blank=True, null=True)
Now I want to create a registration form. I could create a ModelForm based on User but that won't include fields for the Profile and Address (which are required). So what's the best way to go about building this form? Should I even use ModelForm at all?
Furthermore, how would I use the same form for editing the complex object? I could easily pass an instance of Profile back to it, which holds references to the necessary Address and Profile objects, but how do I get it to fill in the fields for me?
What about using 3 separate ModelForm. One for Address, one for User, and one for Profile but with :
class ProfileForm(ModelForm):
class Meta:
model = Profile
exclude = ('user', 'address',)
Then, process these 3 forms separately in your views. Specifically, for the ProfileForm use save with commit=False to update user and address field on the instance :
# ...
profile_form = ProfileForm(request.POST)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
# `user` and `address` have been created previously
# by saving the other forms
profile.user = user
profile.address = address
Don't hesitate to use transactions here to be sure rows get inserted only when the 3 forms are valid.
You should look into the officially recommended way to extend the User model first, as seen in the docs, which I believe comes directly from the project manager's personal blog about the subject. (The actual blog article is rather old, now)
As for your actual issue with forms, have a look at the project manager's own reusable django-profiles app and see if perusing the code solves your issue. Specifically these functions and the views in which they are utilized.
Edited to Add:
I've looked into it a bit (as I needed to do so myself). It seems something like so would be sufficient:
# apps.profiles.models
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
...
birth_date = models.DateField(blank=True, null=True)
joined = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'user profile'
verbose_name_plural = 'user profiles'
db_table = 'user_profiles'
class Address(models.Model):
user = models.ForeignKey(UserProfile)
...
# apps.profiles.forms
from django import forms
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from django.contrib.auth.models import User
from apps.profiles.models import UserProfile, Address
class UserForm(ModelForm):
class Meta:
model = User
...
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
...
AddressFormSet = inlineformset_factory(UserProfile, Address)
I was using "..." to snip content in the code above. I have not yet tested this out but from looking through examples and the documentation on forms I believe this to be correct.
Note I put the FK from the Address model to the UserProfile and not the other way around, as in your question. I believe the inline formsets need this to work correctly.
Then of course in your views and templates you will end up treating UserForm, UserProfileForm, and AddressFormSet separately but they can all be inserted into the same form.
I think your are looking for inline formsets with model forms. This helps you to deal with multiple forms on one page and also takes care of foreign key relations.
Update:
Maybe this question helps you too: Django: multiple models in one template using forms