Django add field to query set - mysql

Hi i have 3 models in my django projects, when user send request i have his dealer_id i need return query set with info about material from Material table and for each row add discount_percent from last model where dealer_id = current dealer_id and row id. Please help me if you have answer.
models
class Material(models.Model):
id = models.AutoField(primary_key=True)
color = models.IntegerField()
name = models.CharField(max_length=100)
material_width = models.IntegerField()
price = models.BigIntegerField(default=0)
class Dealer(models.Model):
id = models.AutoField(primary_key=True)
dealer_name = models.CharField(max_length=100)
dealer_phone = models.CharField(max_length=13, unique=True)
dealer_email = models.CharField(max_length=150, null=False, unique=True)
dealer_firm_name = models.CharField(max_length=100, null=True)
dealer_address = models.CharField(max_length=100, null=True)
dealer_unp = models.CharField(max_length=9, null=True)
dealer_amount = models.FloatField(default=0.0)
user_id = models.BigIntegerField(unique=True)
class MaterialDealerPrice(models.Model):
id = models.AutoField(primary_key=True)
dealer_id = models.BigIntegerField(null=False)
material_id = models.BigIntegerField(null=False)
discount = models.FloatField(null=True, default=0.0)

This looks like a set of models that were automatically created by running inspectdb. You should always treat that output as a first draft; there is a lot that needs to be done manually to tidy it up.
Firstly, your MaterialDealerPrice model needs to have foreign keys to Dealer and Material:
class MaterialDealerPrice(models.Model):
dealer = models.ForeignKey('Dealer', null=False)
material = models.ForeignKey('Material', null=False)
discount = models.FloatField(null=True, default=0.0)
Secondly, you should recognise that this model is in fact the through table of a many-to-many relationship.
class Material(models.Model):
...
dealers = models.ManyToManyField('Dealer', through='MaterialDealerPrice')
Now, you should be able to follow these relationships in your query. Unfortunately your question is not clear enough to know what you actually want to do; you should give an example of the desired output.

Annotate can be used for this purpose. https://docs.djangoproject.com/en/1.11/topics/db/aggregation/

Related

Django order_by('-id') is extremely slow when combined with more than one filter

I have a model Line with a group Foreign key.
I want to get the last 30 Lines under a certain id, within a single group. To do that, I use this query:
Line.objects.filter(group_id=8460, id__lt=10333449).order_by("-id")[:30]
My problem is that this query takes 1.5s to get executed, whereas these three are done almost instantly (<50ms):
Without group_id: Line.objects.filter(id__lt=10333449).order_by("-id")[:30]
Without ordering:
Line.objects.filter(group_id=8460, id__lt=10333449)[:30]
Without id__lt: Line.objects.filter(group_id=8460).order_by("-id")[:30]
For info, I have 10M+ lines in total and about 13k lines in this group alone.
How can I make the query faster when using both id__lt and order_by('-id')?
The model looks like this:
class Line(TransactionSubject):
objects = ModelManager()
group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name="lines")
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
author = models.ForeignKey(Character, on_delete=models.CASCADE, null=True, related_name="lines")
tone = models.CharField(max_length=50, default="neutral")
content = models.TextField(blank=True, null=True)
action = models.TextField(blank=True, null=True)
is_storytelling = models.BooleanField(default=False)
is_chat = models.BooleanField(default=False)
is_description = models.BooleanField(default=False)
is_comment = models.BooleanField(default=False, db_index=True)
character_state = models.ForeignKey(CharacterState, null=True, blank=True, on_delete=models.SET_NULL,
related_name="lines")
created = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True)
whispered_to = models.ManyToManyField(Character, related_name="whispered_lines", blank=True)
yet_to_read = models.ManyToManyField(User, related_name="unread_lines", blank=True)
party = models.ForeignKey(Party, on_delete=models.SET_NULL, null=True, blank=True, related_name="lines")
language = models.ForeignKey(FictionalLanguage, on_delete=models.SET_NULL, null=True, blank=True,
related_name="lines")
beginner_speakers = models.ManyToManyField(Character, related_name="lines_beginner", blank=True)
intermediate_speakers = models.ManyToManyField(Character, related_name="lines_intermediate", blank=True)
fluent_speakers = models.ManyToManyField(Character, related_name="lines_fluent", blank=True)
transactions = GenericRelation(CoinTransaction, related_name="transactions")
tagged_characters = models.ManyToManyField(Character, related_name="lines_tagging", blank=True)
tagged_users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="lines_tagging", blank=True)
An explain() on the query gives:
1 SIMPLE playerdata_line None index_merge PRIMARY,playerdata_line_group_id_429944d8,playerdata__group_id_d5c0b7_idx,playerdata__group_id_667c13_idx,group_id_index playerdata_line_group_id_429944d8,PRIMARY 8,4 None 11677 100.0 Using intersect(playerdata_line_group_id_429944d8,PRIMARY); Using where; Using filesort
The resulting query is:
(1.339) SELECT `playerdata_line`.`id`, `playerdata_line`.`wood`, `playerdata_line`.`copper`, `playerdata_line`.`silver`, `playerdata_line`.`gold`, `playerdata_line`.`platinum`, `playerdata_line`.`legendary`, `playerdata_line`.`total_value`, `playerdata_line`.`group_id`, `playerdata_line`.`user_id`, `playerdata_line`.`author_id`, `playerdata_line`.`tone`, `playerdata_line`.`content`, `playerdata_line`.`action`, `playerdata_line`.`is_storytelling`, `playerdata_line`.`is_chat`, `playerdata_line`.`is_description`, `playerdata_line`.`is_comment`, `playerdata_line`.`character_state_id`, `playerdata_line`.`created`, `playerdata_line`.`edited`, `playerdata_line`.`party_id`, `playerdata_line`.`language_id` FROM `playerdata_line` WHERE (`playerdata_line`.`group_id` = 8460 AND `playerdata_line`.`id` < 10333449) ORDER BY `playerdata_line`.`id` DESC LIMIT 30; args=(8460, 10333449)
Adding an index over fields that are commonly queried together is a good way to speed up those queries. In your case an index over group and id seems appropriate
class Line(TransactionSubject):
# fields...
class Meta:
indexes = [
models.Index(fields=['group', 'id'], name='group_id_index'),
]
I managed to solve it using Iain Shelvington's answer as a base.
Creating the index was the first step, but the problem is that Django was not using that index first.
I fixed it by using the django-mysql extension to force the usage of the index with use_index
Line.objects.filter(group_id=8460, id__lt=10333449).use_index('group_id_index').order_by("-id")[:30]
The query went from 1.4s to 40ms :)

Django - filtering on foreign key on third table

I'm trying to filter a table in Django based on the value of a particular field of a ForeignKey which is a filed of a third table ForignKey.
class A(models.Model):
user_type = models.CharField(blank=True, max_length=32)
description = models.CharField(blank=True,max_length=512)
class B(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT)
name = models.CharField(max_length=32, blank=True)
surname = models.CharField(max_length=32, blank=True)
user_type = models.ForeignKey(A,on_delete=models.SET_NULL, null=True)
class C(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
user_profile = models.ForeignKey(B,on_delete=models.SET_NULL, null=True)
test = models.CharField(blank=False,max_length=512)
here is the query that I wish to make :
I want to query on the C and find the user_type_id on the B then filter user_type value on A
something like this (just for showing what I want):
models.C.objects.filter(test="test").filter(B__user_type_id__A__user_type = 1)
final result:
I want to get all of the data that test="test" in table C and user_type = 1 in table A
Since the user_type field on the A model is a CharField. So you can not filter on a number. You can however filter on a value of that user_type field:
C.objects.filter(test='test', b__user_type__user_type='my_user_type_value')
Or you can filter on the primary key of the A object:
C.objects.filter(test='test', b__user_type_id=1)

How to create a table with non-unique id and have it as foreign key in another table?

I have the following tables in models.py:
class Part(models.Model):
partno = models.CharField(max_length=50, unique=True)
partdesc = models.CharField(max_length=50, default=None, blank=True, null=True)
class Price(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE, null=True)
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
qty = models.IntegerField(default=1)
price = models.DecimalField(max_digits=9, decimal_places=2)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
datestart = models.DateTimeField(auto_now_add=True, blank=False, null=False)
class Meta:
unique_together = (('supplier', 'part'),)
This is properly working. The problem is I have many part numbers which are their replacements. For example part 1001-01, 1001-02, 1001-03 are all the same part. Still, I have all of them in my Part table.
I need to match them in another table, so I don't need to enter price for each of them separately. There must be a unique key representing all of these three items.
Question: How do I setup a "part number match table" and have a foreign key to this table in my Price table?
(Rest is optional to read which are my opinions/problems so far, might help though)
1: I tried to setup the table like this:
class PartMatch(models.Model):
id = models.IntegerField(primary_key=True, unique=False, null=False, db_index=True)
part = models.ForeignKey(Part, unique=False, on_delete=models.CASCADE)
I don't get an error while migrating but when I try to use the same id for PK, it doesn't allow me.
2: I left the pk alone and tried to setup another field to match parts:
class PartMatch(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE)
partmatchid = models.IntegerField(null=True, unique=False, db_index=True)
I don't get any error while migrating but when I try to use partmatchid as a foreign key in my Price table like this:
partmatch = models.ForeignKey(PartMatch, to_field="partmatchid",
db_column="partmatchid",
on_delete=models.CASCADE)
I get an error saying foreign key must be unique while migrating.
Well in this case I am out of solutions. I wonder how you guys handle this?
Your Part model can have a foreign key to PartMatch, not the other way around.
Your model can be Part *<-->1 PartMatch and PartMatch 1<-->1 Price
For example:
class Price(models.Model):
price = models.DecimalField(max_digits=9, decimal_places=2)
class PartMatch(models.Model):
price = models.OneToOneField(Price, primary_key=True)
class Part(models.Model):
partno = models.CharField(max_length=50, unique=True)
partMatch = models.ForeignKey(PartMatch)
If you want to get all parts with some price, some_price.partmatch.part_set.all() will do the job.

SQLAlchemy: Trouble querying in manyto many relationship

New to SQLalchemy, an sql queries in general but hopefully this will be clear to someone :) In a Flask application, I have two models, User and classes, in a many to many relationship
Here is the models.py
user_to_classes = db.Table('user_to_classes', Base.metadata,
db.Column('class_id', db.Integer, db.ForeignKey('classes.id')),
db.Column('user_id', db.Integer, db.ForeignKey('users.id'))
)
class Classes(db.Model):
__tablename__= 'classes'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
date = db.Column(db.DateTime)
participants = db.relationship('User', secondary=user_to_classes, backref = db.backref('classes',lazy='dynamic'))
classtype_id = db.Column(db.Integer, db.ForeignKey('classtype.id'))
status = db.Column(db.Integer) #1 = open, 0 = closed
class User(UserMixin,db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(64),unique=True,index=True)
firstname = db.Column(db.String(64))
lastname = db.Column(db.String(64))
fullname = db.Column(db.String(64), index=True)
telephone = db.Column(db.String(64))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
notes = db.Column(db.Text())
punchcard_passes = db.Column(db.Integer)
I am trying to know how many classes attended a user. I have no problem querying how many users participated in a class, but not the reverse as there is no value to query in the user model. Is it even possible? Not being fluent in SQL queries, I am not sure what to search for either on google. All the exemples I have seen do a one way query, and never the other way.
thanks!
How about len(user.classes) ? Doesn't it work ?
In addition: don't name a class in plural, since an object of it represents only one class.

Designing a database structure with Django

I am trying to design a database structure for a website that offers people to open a personal profile.
I am trying to figure out if the design that I've chosen is good enough.. The reason i suspect it might be problematic is that I use many relations between many tables. That way, when a person's page is being loaded, there are many JOINs behind the scenes and that will surely create a bottleneck. I would appreciate if you can help me figuring out if the design is proper or if I should reconsider it.
So I have come up with the following design:
class Person(models.Model):
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
name = models.CharField(max_length=200)
# Each person can have one profession
profession = models.ForeignKey(Profession)
email = models.CharField(max_length=100, blank=True)
website = models.CharField(max_length=200, blank=True)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES))
birth_date = models.DateField(blank=True, null=True)
class Profession(models.Model):
type = models.CharField(max_length=20, blank=False, choices = ("Teacher","Pilot","Politician"))
#Each person can have several geolocations (represent the person's home/office/other address)
class Geolocation(models.Model):
latitude = models.FloatField()
longitude = models.FloatField()
address = models.TextField()
related_person = models.ForeignKey(Person, blank=False, null=False)
#Each person can have several medias (you tube movies)
class Media(models.Model):
youtube_id = models.CharField(max_length=100)
related_person = models.ForeignKey(Person, blank=False, null=False)
#Each person can have several websites
class Websites(models.Model):
website_url = models.CharField(max_length=200, blank=False)
website_name = models.CharField(max_length=200)
related_person = models.ForeignKey(Person, blank=False, null=False)
#See below...
related_link_farm = models.ForeignKey(LinkFarms, blank=True, null=True)
#Each website that refers to a person must be also refered to a link farm
class LinkFarms(models.Model):
farm_name = models.CharField(max_length=200, blank=False)
Notice that for each page loaded there are at least 5 table joins.
Thanks,
Meir
It's ok to normalise data. That's what relational databases are for! Your design looks fine to me. If it becomes a problem you can always cache page renders.
Maybe you could remove profession table like this:
class Person(models.Model):
...
TEACHER_ID = 'te'
PILOT_ID = 'pi'
POLITITAN_ID = 'po'
PROFESSION_CHOICES = (
(TEACHER_ID, 'Teacher'),
(PILOT_ID, 'Pilot'),
(POLITITAN_ID, 'Polititian')
)
...
profession = db.CharField(max_length=2, choices=PROFESSION_CHOICES)
Then you can query all polititian like this:
polititans = Person.objects.filter(profession=Person.POLITITAN_ID)