Improving run time of simple query - python

Gurus:
I have a very simple data model relating two different kinds of Users via an Interaction:
# myapp/models.py
class C1(models.Model):
user = models.ForeignKey(User)
class C2(models.Model):
user = models.ForeignKey(User)
class Interaction(models.Model):
c1 = models.ForeignKey(C1)
c2 = models.ForeignKey(C2)
date = models.DateField()
So, an Interaction has a User of class C1, a User of class C2, and a date (in addition to the primary key, automatically an integer); a given pair of users can have many Interactions.
I populated the database with 2000 random Users (1000 each class), and when I query the Interaction the run time is too slow (about three seconds - unacceptable in production environment).
Is there something I can do to improve the run time of this search? Should I define the Interaction differently?
Thanks.

If you'd like to store additional information related to your users, Django provides a method to specify a site-specific related model -- termed a "user profile" -- for this purpose.
To make use of this feature, define a model with fields for the additional information you'd like to store, or additional methods you'd like to have available, and also add a OneToOneField from your model to the User model. This will ensure only one instance of your model can be created for each User. For example:
# settings.py
AUTH_PROFILE_MODULE = 'myapp.UserProfile'
# myapp/models.py
class UserProfile(models.Model):
CLASS_CHOICES = (
(0, 'Yellow User'),
(1, 'Green User'),
)
user_class = models.IntegerField(choices=CLASS_CHOICES)
user = models.OneToOneField(User)
class Interaction(models.Model):
u1 = models.ForeignKey(User, related_name='u1s')
u2 = models.ForeignKey(User, related_name='u2s')
date = models.DateField()
Creating a new model and associated table for each User class seems not like good design.

You have used foreign keys to associate C1, C2 with Users, and called this a one-to-many relationship. However, the relationship between Interaction C1, C2 is not one-to-many because one Interaction can be associated with many Users, and one User can also have many Interactions associated with it. This is called a many-to-many relationship, and is represented in Django models using models.ManyToManyField.
So try changing your models.py file to -
class Interaction(models.Model):
ic1 = models.ManyToManyField(C1)
ic2 = models.ManyToManyField(C2)
date= models.DateField()
See if this helps...

Related

Get all the rows of a table along with matching rows of another table in django ORM using select_related

I have 2 models
Model 1
class Model1(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
type = models.CharField(max_length=255)
details = models.TextField(max_length=1000)
price = models.FloatField()
Model 2
class Model2(models.Model):
id = models.IntegerField(primary_key=True)
user_id = models.ForeignKey(
User,
on_delete=models.CASCADE
)
plan_selected = models.ForeignKey(Model1)
I am trying to check whether a user has selected any plans.
The field plan_selected is a foreign key for Model1 - id. I want to get all details of Model1 along with details of Model2 in a single line of the query set using Django.
So far I have tried to get is :
sub_details = Model1.objects.select_related('Model2').filter(user_id=id)
For select_related(), you want to select on the field name, not the related model's name. But all this does is that it adds a join, pulls all rows resulting from that join, and then your python representations have this relation cached (no more queries when accessed).
You also need to use __ to traverse relationships across lookups.
Docs here: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related
But you don't even need select_related() for your goal: "I am trying to check whether a user has selected any plans.". You don't need Model1 here. That would be, given a user's id user_id:
Model2.objects.filter(user_id=user_id).exists()
# OR if you have a User instance `user`:
user.model2_set.exists()
If however what you want is "all instances of Model1 related to user via a Model2":
Model1.objects.filter(model2__user_id=user_id).all()
to which you can chain prefetch_related('model2_set') (this is 1 -> Many so you're pre-fetching, not selecting - i.e fetches and caches ahead of time each results' related model2 instances in one go.)
However, that'd be easier to manage with a ManyToMany field with User on Model1, bypassing the need for Model2 entirely (which is essentially just a through table here): https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

How do I add a polymorphic class in Django?

I am trying to make an application for a restaurant in django; I have to create a menu for different types of items, and all of those different types of items have to essentially be a product, so that I can add that product to a user's corresponding cart. Here are my menu items:
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
price = models.DecimalField(decimal_places=2, max_digits=10)
class Pizza(Product):
pizzatype = models.CharField(max_length=15)
extras = models.TextField(max_length=50)
size = models.CharField(max_length=10)
class Subs(Product):
name = models.TextField(max_length=64)
size = models.CharField(max_length=10)
class DinnerPlatters(Product):
name = models.TextField(max_length=64)
size = models.CharField(max_length=10)
class Pasta(Product):
name = models.TextField(max_length=64)
class Salads(Product):
name = models.TextField(max_length=64)
As can be seen, I tried deriving the models for different types of menu items from a single model Product, but while running makemigrations, I get the following message on the terminal:
You are trying to add a non-nullable field 'product_ptr' to dinnerplatters without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
I tried googling the solution to my problem, but couldn't find an approriate solution.
I am new to Django and this is my first application, so any help or any other method on how to approach my problem would be kindly appreciated.
As your models inherit from the Product's model, you are using Multi-table inheritance. Now that Multi-table inheritance automatically creates OneToOneField as classname_ptr between inheritance model and base model, your models which is inherit from product's model has product_ptr field. As your model has product_ptr field, you should add default value for existing rows which is save before by you.
https://docs.djangoproject.com/en/3.0/topics/db/models/#multi-table-inheritance

Multiple User type with Django

Using Django we have two types of Users (Teachers and Students) with common fields and uncommon fields
In our wizard we first POST the common fields to /users with an extra type_field
Every operation after this should be able to figure out which model (Teacher or Student) it needs to use.
We are thinking of making two models ( Teacher and Student ) with an one-to-one field.
But how do we hookup the type_field to the right Model on every operation?
You dont have to go for an extra field since you are already having two different classes for students and teachers. A simple approach may looks like below.
from django.contrib.auth.models import User
class Teacher(User):
extra_field_1 = models.Fieldtype()
...
...
class Student(User):
extra_field_1 = models.Fieldtype()
...
...
You can provide both type of users same registration form and upon clicking next take them to next page based on the value of I am a teacher/student field. In that case I suggest you to use atomic blocks if you dont want to save data in case registration procedure fails at some point or user have selected a wrong choice and they want to go back. By this approach each inherited models have username, first_name, last_name and email that you dont have to insert any of these to Teacher or student model.
Then you have to create forms for each model. You may use modelform A much better approach will be using class based views since that reduce a lot of code and stick to dry principles.
You may use something like:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
# more common fields
class Student(Person):
specific_field_to_student = models.CharField(max_length=255)
# more fields
class Teacher(Person):
specific_field_to_teacher = models.CharField(max_length=255)
# more fields
In your database you will have 3 tables (yourapp_person, yourapp_student and yourapp_teacher). Now, if type_field value is student, you will use Student model to create user, if it is teacher, you will use Teacher model.
Note: You may need to make Person model above a subclass of the built-in User model.
Edit:
I have edited the model above to take into account the requirements in the comments below.
Now, to retrieve user by id, you can use the following code in your view:
user = Person.objects.get(id=id) # id is the view param
if hasattr(user, 'student'):
print("Student")
else: # hasattr(user, 'teacher')
print("Teacher")

Best way to handle multiple type of users in Django

In my project I have to deal with different type of users e.g. costumers and employers. Each type of user have its own fields and permissions: costumer can buy things whereas employer can not.
I have read the Django docs and it looks like there are two options:
Costumize the AbstractUser class and adding all the fields of costumers and employers. Then use the permission system to grant/revoke permission or create a group for each type of user. The downside here is that there unused fields.
Adopt the proxy model:
from django.contrib.auth.models import User
from django.db import models
class Costumer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'costumers'
permissions = (
("access_this", "User may access this"),
)
ordering = []
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class Meta:
db_table = 'employers'
permissions = (
("access_that", "User may access that"),
)
ordering = []
This case seems more reasonable but I don't know how to deal with the permissions. Consider I'd like to use #permission_required instead of checking the type (if the user has a specific field) because it seems more legit for Django system.
So in the end, what is the best way to approach such scenario?
The first solution is much better.
The downside here is that there unused fields.
I disagree with this, you don't have to store all the fields within the User model. Also, if you're talking about 5 fields for example, that doesn't really matter.
You can extend AbtractUser and also use some composition; you don't have to put all the fields there:
class User(AbstractUser):
email = ...
...
# for the employer, let's say you want to save the company details
company = models.ForeignKey('myapp.Company', null=True, blank=True)
...
# for the customer
customer_details = models.ForeignKey('...')
This way you could record a user_type if you want or deduce the type from the foreign key (if there is a company, it's an employer).
To help you more with the model, I need to know what differentiate an employer from a customer in your application. Note that with that solution, an instance of User could be both.
Concerning the permissions, I feel like it's a separate problem; I'd recommend you to sort it last. If the design you pick is close to the reality and you get the features working; adding custom permissions will be really easy.

How do you force only one relationship in django when multiple are possible?

I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.

Categories