Django: extending User model avoiding DB join - django

In my Django app, I need to create a User Model with some extra fields. For DB performance, I would like to avoid to make a join everytime I want to access those fields. At DB level I would like to produce an ALTER TABLE on the user table to add those fields, or even a CREATE TABLE with all the fields I need on the app initialization would be fine, since I'm still in development.
I've found two solutions to extend the User Model (reported below) but both are inconsistent with my choice of avoiding JOINs.
models.py (Solution 1)
from django.db import models
class Person(models.Model):
# Aggregate using a OneToOneField on User
user = models.OneToOneField(User)
age = models.PositiveSmallIntegerField()
models.py (Solution 2)
from django.contrib.auth.models import User
class Person(User):
# Inheriting from User
age = models.PositiveSmallIntegerField()
SQL (Solution 1)
/* Aggregate using a OneToOneField on User */
BEGIN;
CREATE TABLE "people_person" (
"id" integer NOT NULL PRIMARY KEY,
"user_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id"),
"age" smallint unsigned NOT NULL
);
COMMIT;
SQL (Solution 2)
/* Inheriting from User */
BEGIN;
CREATE TABLE "people_person" (
"user_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "auth_user" ("id"),
"age" smallint unsigned NOT NULL
);
COMMIT;

From django 1.5, it is possible to replace the user model rather than extending it.
https://docs.djangoproject.com/en/dev/releases/1.5/#configurable-user-model
In django 1.4 or older, there the "profile" attribute on the user gets some special caching, which may or may not be good enough for your requirements.

Related

Which database design is better for django follow/unfollow?

I've been looking for a good database design for a twitter like social network site in my django project and I found two possibilities:
This one down here
class Following(models.Model):
follower = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='following')
following = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='followers')
And this other one
class User(AbstractUser):
follows = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followed_by')
pass
Are these the same? Is there any difference here? Which one should I choose? I'm kind of new to this so I can`t figure out which one is the best option. I find the first one easier to understand.
If I add this to my user model
following = models.ManyToManyField('self', related_name="followers")
and run (assuming auth is the app where your user model is, and replacing 000X by the number of the generated migration)
python manage.py makemigrations auth
python manage.py sqlmigrate auth 000X
this is what I get:
CREATE TABLE `auth_user_following` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`from_user_id` integer NOT NULL, `to_user_id` integer NOT NULL);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user__from_user_id_b9318b74_fk_auth_`
FOREIGN KEY (`from_user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user__to_user_id_b51bc961_fk_auth_`
FOREIGN KEY (`to_user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user_foll_from_user_id_to_au_88cd5a29_uniq`
UNIQUE (`from_user_id`, `to_user_id`);
So it creates a table with an auto-generated id and two foreign key columns, just as it would do with the explicit relation-only model, i.e. on the database side, there is no structural difference.
For code readability, I would much prefer to keep the relation in the model and not define it in a different class. However, if you want to add additional data to the relation (e.g. date_started_following), you will need an explicit relation model. Then, you might still want to mention this many-to-many-relation in your user model and point to the explicit relation using the through argument:
However, sometimes you may need to associate data with the
relationship between two models.
[...]
Django allows you to specify the model that will
be used to govern the many-to-many relationship. You can then put
extra fields on the intermediate model. The intermediate model is
associated with the ManyToManyField using the through argument to
point to the model that will act as an intermediary.
One other reason for the first approach or an explicit through model is that it might facilitate some queries about the relationship, e.g. "find users who follow each other".
I would suggest both models code will work fine.
If you want to create custom user model with new fields then use below code format.
AbstractUser: Use existing fields in the user model
AbstractBaseUser:In case want to create your own user model from
scratch
class User(AbstractUser):
follows = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followed_by')
pass
You want to segregate your app related changes then use below models code.
class Following(models.Model):
follower = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='following')
following = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='followers')

Can i change column type in database without updating Django model?

I have a django project which has created models for example:
from django.db import models
class Person(models.Model):
title = models.CharField(max_length=30)
description = models.CharField(max_length=30)
This creates table:
CREATE TABLE myapp_person (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(30) NOT NULL,
"description" varchar(30) NOT NULL
);
Now i'm wondering can i change the column type to "LONGTEXT" without having to change the model? Would this cause an error? I don't have access to django code but i have access to database and i'm importing data, and values are more than 30 characters.
Would this cause an error?
Likely not, since it will still communicating strings from and to the database.
I don't have access to django code but i have access to database and i'm importing data, and values are more than 30 characters.
Well depending on how the logic is implemented to create records, this can be a problem. A ModelForm for example, or a Serializer, will often perform validation, and thus check if the number of characters is indeed less than or equal to 30, if not it will not accept these values.
That being said, it is often a (very) bad idea to let the models and the database run out of sync. Therefore it is probably better to change the field to a TextField, make migrations, migrate the database, and run the program:
from django.db import models
class Person(models.Model):
title = models.CharField(max_length=30)
description = models.TextField()
I would advise not to alter the database and keep the Django data unchanged, since you eventually run into a situation where the database will accept other data than what the Django model anticipated on.

Django - "ManyToMany" without primary key (AutoField)

Is it even possible to create a model (or a many to many relation) in Django without id field (AutoField) in database?
For example, I have models: Task and User. User can have assigned many Tasks and Task can be assigned to many Users.
Generally, Django will create the relationship table with fields like id, user_id, task_id. Can a id field be omitted? The user_id and task_id fields will be marked as unique_together.
No, it is not possible to create a many to many field with just the user_id and task_id fields.
All Django models must have exactly one primary key field. It is not yet possible to use composite primary keys (e.g. (user_id, task_id)).

Foreign Fields in Django

I am currently working on a Django project.
I am using Foreign Fields in one of the models.
class Purchase (models.Model):
user = models.ForeignKey(User, blank = True, null = True)
game = models.ForeignKey(Game)
...
And accessing these values in views like
p = Purchase.objects.filter(user=request.user,game=request.POST['gameid'])
In DB(i am using Postgres), the fields in Purchase Table are user_id and game_id
respectively for user and game.
I think its the Django default to suffix foreign fields with _id.
So i tried a bit in manage.py shell
and i came to know that i am getting same amount of results even if i use
p = Purchase.objects.filter(user_id=request.user,game_id=request.POST['gameid'])
So my doubt is that whether the field names defined in model and exact names of those fields in DB can be used interchangeably? Any further information or explanation would be appreciated.
Thanks
Django does this for foreign key fields. If you want to have the same name in the database then you can define column name using db_column:
user = models.ForeignKey(User, db_column='user')
P.S you can use both user_id and user to reference foreign key. This is absolutely fine. But i will suggest you to use model level names to avoid confusions.

Django get the value of foreignId from django.contrib.auth.models User

I have created a model which has foreign key to the django.contrib.auth.models User model. I need to retrieve the value the foreign key is referring to. But how to do that?
E.g. my model is
from django.contrib.auth.models import User
def FooModel(models.Model):
user = models.ForeignKey(User)
then I know I can either use:
FooModel.objects.get() or FooModel.objects.filter() but as far as I know they will only return entries from the FooModel only. So how do I retrieve from the User model?
m = FooModel.objects.get(id=123434)
m.user.username
You can use ..That wxample will return you the username of the related user. Also you can create joins using __ on the query filtering. Like:
FooModel.objects.get(user__username='Foo')
But, since you are building your query on FooModel, you will always get FooModel results. So you have you use . to use foreignkey relations to get to the required User field.
EDIT:
Django also allows you to use reverse relations on querysets, so even though User model do not have a foreignkey to FooModel on its model structure, you can use reverse relation of Foomodel.user foreignkey like:
def FooModel(models.Model):
user = models.ForeignKey(User)
foofield = Models.CharField(...)
User.objects.get(foomodel__foofield='Beatles')
will return you the user which have FooModel record with a foreign key to his User record and foofield value of Beatles
You can access foreign key ids by referencing the property name + _id. For example: yourmodel.user_id