Convert SQL query in Django model format - sql

I'm trying to convert an SQL query into django format but as I'm quite new to django I'm having some trouble.
My query:
select make_name, count(*) as count from main_app_make as make
join main_app_model as model on make.id = model.make_id
join main_app_vehicle as vehicle on model.id = vehicle.model_id
group by make.make_name
The result:
Audi 1
Mercedes-Benz 2
My models:
class Make(models.Model):
make_name = models.CharField(max_length=50)
make_logo = models.CharField(max_length=400)
def __str__(self):
return self.make_name
class Model(models.Model):
model_name = models.CharField(max_length=50)
make = models.ForeignKey(Make, on_delete=models.CASCADE)
def __str__(self):
return self.model_name
class Vehicle(models.Model):
type = models.ForeignKey(VehicleType, on_delete=models.CASCADE)
model = models.ForeignKey(Model, on_delete=models.CASCADE)
body_type = models.ForeignKey(Body, on_delete=models.CASCADE)
...
This is what I tried:
options = Make.objects.all().values('make_name').annotate(total=Count('make_name'))

I think you need to include the children models in the Count :
options = Make.objects.values('make_name').annotate(total=Count('model_set__vehicle_set'))
Reference : https://docs.djangoproject.com/en/stable/topics/db/aggregation/#following-relationships-backwards

Related

DRF- how to do many readonly and writeonly fields in serializers

I used two foreignkey in my model. I want to show those fields name when we give get request I have tried but its worked only one fields not rest one.
models.py
class Organization(models.Model):
code = models.CharField(max_length=25, null=False, unique=True)
name = models.CharField(max_length=100, null=False)
location = models.ForeignKey(Location, on_delete=models.RESTRICT)
mol_number = models.CharField(max_length=100)
corporate_id = models.CharField(max_length=100)
corporate_name = models.CharField(max_length=100)
routing_code = models.CharField(max_length=100)
iban = models.CharField(max_length=100)
description = models.TextField()
total_of_visas = models.IntegerField(null=False, default=0)
base_currency = models.ForeignKey(Currency, on_delete=models.RESTRICT)
def __str__(self):
return self.name
serializers.py
class OrganizationSerializer(serializers.ModelSerializer):
location = serializers.CharField(read_only=True, source="location.name")
base_currency = serializers.CharField(read_only=True, source="base_currency.currency")
location_id = serializers.IntegerField(write_only=True, source="country.id")
base_currency_id = serializers.IntegerField(write_only=True, source="base_currency.id")
class Meta:
model = Organization
fields = ["id", "name", "location", "mol_number", "corporate_id", "corporate_name",
"routing_code", "iban", "description", "total_of_visas", "base_currency",
"location_id", "base_currency_id"]
def create(self, validated_data):
...
def update(self, instance, validated_data):
...
How can I access those two fields???.. Anyhelp Appreciable..
you can override the to_representation method
def to_representation(self, instance):
....
so something like this:
class OrganizationSerializer(serializers.ModelSerializer):
...
class Meta:
...
def to_representation(self, instance):
rep = super(OrganizationSerializer, self).to_representation(instance)
rep['location'] = instance.location.name //the .name is the field in the //location model that you want to return it can be anything in the model
rep['base_currency'] = instance.base_currency.currency
rep['location_id'] = instance.location_id.country.id
rep['base_currency_id'] = instance.base_currency_id.base_currency_id
return rep
def create(self, validated_data):
...
def update(self, instance, validated_data):

SQLAlchemy ORM: Filter by multiple include/exclude matches on many-to-many relationship

I'm trying to use an SQLite database via SQLAlchemy 1.4 ORM to filter data based on relationships.
Data
My example data consists of groups and members in a simple many-to-many schema:
[...]
member_f = Member(name="F")
group_1 = Group(name="1", members=[member_a, member_b, member_c]) <-- find this group via member names
group_2 = Group(name="2", members=[member_a, member_b, member_f])
group_3 = Group(name="3", members=[member_a, member_c, member_d])
group_4 = Group(name="4", members=[member_d, member_e, member_f])
[...]
Full running example code (schema, objects, queries):
from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy import create_engine, select, func, text, and_
from sqlalchemy.orm import relationship, declarative_base, sessionmaker
engine = create_engine("sqlite+pysqlite:///:memory:", future=True, echo=False)
Session = sessionmaker(bind=engine, future=True)
# Schema
Base = declarative_base()
groups_members = Table("groups_members", Base.metadata,
Column("group_id", ForeignKey("groups.id")),
Column("member_name", ForeignKey("members.name")),
)
class Group(Base):
__tablename__ = "groups"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False)
members = relationship("Member", secondary=groups_members, backref="groups", lazy="subquery")
def __repr__(self):
return f"<Group: {self.name}>"
class Member(Base):
__tablename__ = "members"
name = Column(String, primary_key=True)
def __repr__(self):
return f"<Member: {self.name}>"
Base.metadata.create_all(engine)
# Objects
member_a = Member(name="A")
member_b = Member(name="B")
member_c = Member(name="C")
member_d = Member(name="D")
member_e = Member(name="E")
member_f = Member(name="F")
group_1 = Group(name="1", members=[member_a, member_b, member_c])
group_2 = Group(name="2", members=[member_a, member_b, member_f])
group_3 = Group(name="3", members=[member_a, member_c, member_d])
group_4 = Group(name="4", members=[member_d, member_e, member_f])
print(f"{member_a}: {member_a.groups}") # OK
with Session() as session:
session.add(group_1)
session.add(group_2)
session.add(group_3)
session.add(group_4)
session.commit()
print(session.query(Group).all()) # OK
# Query users example
def get_members_in_more_than_2_groups():
with Session() as session:
return session.execute(
select(Member, func.count(groups_members.columns.group_id).label('group_members_count'))
.join(groups_members)
.group_by(Member.name)
.having(text('group_members_count > 2'))
).all()
for m in get_members_in_more_than_2_groups():
print(m) # OK
# Query groups problem: associated with A and B but not with E or F
def get_groups_by_member_names(member_names_included, member_names_excluded):
with Session() as session:
included = session.execute(select(Member).where(Member.name.in_(member_names_included))).all()
excluded = session.execute(select(Member).where(Member.name.in_(member_names_excluded))).all()
return session.execute(
select(Group)
.join(Group.members)
.where(
and_(
Group.members.contains(included),
~Group.members.contains(excluded),
)
)
.group_by(Group.id)
).scalars().all()
for g in get_groups_by_member_names(member_names_included=["A", "B"], member_names_excluded=["E", "F"]):
print(g) # Expected output: <Group: 1>
Goal
Now I'm trying to find all groups that
have both members with the names A and B (that's groups 1 and 2)
and don't have any member named E or F (removing group 2)
resulting in just group 1.
Problem
The relevant (and failing) function in the example code is get_groups_by_member_names and with my lack of database knowledge, I'm quite stuck.
Most existing questions that I could find on SO only need to filter by one relationship value. But I need them to consider the lists of included and excluded member names.
I have tried to get the members as SQLAlchemy objects first and inserting those into the query but without any luck. I may have done that completely wrong, though.
I also tried joining the tables, filtering with the names list and counting the grouped results... It's hard for me to tell whether I'm on the right track or not at all.
Running over all groups in Python and applying the filtering there would be my fallback workaround. But with many items, the database can probably handle it more efficiently.
Any help greatly appreciated, I am happy with anything that works. I could probably also work my way up from a functioning SQL statement.
Thanks for your time!
Edit 1:
I found this answer https://stackoverflow.com/a/21104689/5123171 and while it works on small data sets, it's terribly slow on larger ones (about 60 seconds for 500 members and 10k groups):
def get_group_by_members(member_names_included, member_names_excluded):
with Session() as session:
return session.query(Group).join(groups_members).filter(
groups_members.columns.member_name.in_(member_names_included)).group_by(Group.id).having(func.count(groups_members.columns.member_name) == len(member_names_included),
).filter(
~Group.members.any(Member.name.in_(member_names_excluded)),
).all()
Ok, here's what I ended up with via trial & error in an SQL editor. This is faster than the previous attempt (50 milliseconds on the same data).
The comments in the code correspond to the following steps:
Find groups containing excluded members
Filter those out of the assignment table
Filter remaining assignment table by included members
Group by group IDs
Return all remaining groups matching the number of included members
SQL
SELECT * FROM groups
WHERE id IN (
SELECT group_id FROM groups_members
WHERE member_name IN ("A", "B") // 3.
AND group_id NOT IN ( // 2
SELECT group_id FROM groups_members
WHERE member_name IN ("E", "F") // 1.
)
GROUP BY group_id // 4.
HAVING count(member_name) == 2 // 5.
)
SQLAlchemy
session.query(Group)
.where(Group.id.in_(
session.query(groups_members.c.group_id)
.where(
groups_members.c.member_name.in_(member_names_included), # 3.
groups_members.c.group_id.not_in( # 2.
session.query(groups_members.c.group_id).where(
groups_members.c.member_name.in_(member_names_excluded) # 1.
)
),
)
.group_by(groups_members.c.group_id) # 4.
.having(
func.count(groups_members.c.member_name)
== len(member_names_included) # 5.
))
)
.all()
And the full running example in one piece:
from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy import create_engine, select, func, text, and_, not_
from sqlalchemy.orm import relationship, declarative_base, sessionmaker
engine = create_engine("sqlite+pysqlite:///:memory:", future=True, echo=False)
Session = sessionmaker(bind=engine, future=True)
# Schema
Base = declarative_base()
groups_members = Table(
"groups_members",
Base.metadata,
Column("group_id", ForeignKey("groups.id")),
Column("member_name", ForeignKey("members.name")),
)
class Group(Base):
__tablename__ = "groups"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False)
members = relationship(
"Member", secondary=groups_members, backref="groups", lazy="subquery"
)
def __repr__(self):
return f"<Group {self.id}: {self.name}>"
class Member(Base):
__tablename__ = "members"
name = Column(String, primary_key=True)
def __repr__(self):
return f"<Member: {self.name}>"
Base.metadata.create_all(engine)
# Objects
member_a = Member(name="A")
member_b = Member(name="B")
member_c = Member(name="C")
member_d = Member(name="D")
member_e = Member(name="E")
member_f = Member(name="F")
group_1 = Group(name="1", members=[member_a, member_b, member_c])
group_2 = Group(name="2", members=[member_a, member_b, member_f])
group_3 = Group(name="3", members=[member_a, member_c, member_d])
group_4 = Group(name="4", members=[member_d, member_e, member_f])
with Session() as session:
session.add(group_1)
session.add(group_2)
session.add(group_3)
session.add(group_4)
session.commit()
# Query
member_names_included = ["A", "B"]
member_names_excluded = ["E", "F"]
# Raw SQL variant
with Session() as session:
result = session.execute(
f"""
SELECT * FROM groups
WHERE id IN (
SELECT group_id FROM groups_members
WHERE member_name IN ("A", "B")
AND group_id NOT IN (
SELECT group_id FROM groups_members
WHERE member_name IN ("E", "F")
)
GROUP BY group_id
HAVING count(member_name) == 2
)
"""
).all()
groups = [Group(**r) for r in result]
for r in groups:
print(f'SQL {r}')
# ORM Variant
with Session() as session:
result = (
session.query(Group)
.where(
Group.id.in_(
session.query(groups_members.c.group_id)
.where(
# Matching any included members
groups_members.c.member_name.in_(member_names_included),
# Removing any groups containing excluded members
groups_members.c.group_id.not_in(
session.query(groups_members.c.group_id).where(
groups_members.c.member_name.in_(member_names_excluded)
)
),
)
# This is to make sure that all included members exist in a group, not just a few
.group_by(groups_members.c.group_id)
.having(
func.count(groups_members.c.member_name)
== len(member_names_included)
)
)
)
.all()
)
for r in result:
print(f'ORM {r}')
I hope this is helpful to anyone and if you have suggestions for improvement, please let me know.

odoo-How to save related fields for one2many relation

my problem is that when i saved the fields in model's from B , i did't saw the result in model's view A execept the name of empoyee and department becuse declared in the same model, some freinds suggeste me to use onchnage function but how !!
class FeuilleTemps(models.Model): # A
_name = 'tbrh.feuilletemps'
_rec_name = 'name_emp'
name_emp = fields.Many2one('hr.employee', string="Employé")
name_dep = fields.Many2one('hr.department', string="Département")
abscence_ids = fields.One2many('tbrh.abscences', 'feuille_id', string="ma liste ")
relation_id = fields.Many2one('tbrh.abscences')
date2 = fields.Date(related='relation_id.date', store=True, use_parent_address=False)
statut = fields.Selection(related='relation_id.statut', store=True)
class Abscences(models.Model): # B
_name = 'tbrh.abscences'
statut = fields.Selection([('abscent', 'Abscent'), ('present', 'Présent')], string="Statut")
date = fields.Date()
feuille_id = fields.Many2one('tbrh.feuilletemps',
ondelete='cascade', string="feuille ", required=True)

django complicated agregate in three tables

I have a three tables with one main (Transaction) and two dependant(TransactionDialogMessages, TransactionDialogLastAccess).
class Transaction(models.Model):
status = models.CharField(max_length = 3, default = 'NEW')
user1 = models.ForeignKey(User, related_name='user1', blank=True, null=True, on_delete=models.SET_NULL)
user2 = models.ForeignKey(User, related_name='user2', blank=True, null=True, on_delete=models.SET_NULL)
some_info = models.DateTextField()
class TransactionDialogMessages(models.Model):
transaction = models.ForeignKey(Transaction)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
message = models.TextField()
create_datetime = models.DateTimeField(auto_now = True)
class TransactionDialogLastAccess(models.Model):
transaction = models.ForeignKey(Transaction)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
access_time = models.DateTimeField()
I want to create one query with complex agregation on this three tables, and result in hash (or queryset) with such structure:
{'transaction_id', 'status', 'user1', 'user2', 'some_info', 'count_of_new_messages'}
where count_of_new_messages is agregation field, which computed as
count (TransactionDialogMessages(transaction = transaction_id, user = request.user, create_datetime > TransactionDialogLastAccess(transaction = transaction_id, user = request.user).access_time))
Is it real?
Updated:
I created SQL query, which serves my purpose:
SELECT a.id, a.status, ... , b.COUNT_OF_NEW_MESSAGES FROM transaction a
LEFT JOIN (SELECT b.transaction_id, COUNT(b.transaction_id) AS COUNT_OF_NEW_MESSAGES
FROM transactiondialogmessages b
LEFT JOIN transactiondialoglastaccess c
ON b.transaction_id = c.transaction_id
WHERE b.create_datetime > c.access_time OR c.access_time IS NULL
GROUP BY b.transaction_id ) b
ON a.id = b.transaction_id;
But how it's realize on django orm (without raw)?

Is there anything like unique_together(max_occurences=3)?

I have a model:
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
c = models.IntegerField()
Now, I need something like unique_together(a,b, max_occurences=3) constraint to be added to the above model (so that there can be up to 3 values of c for each pair of (a,b), and ideally those 3 values of c should be also unique for given (a,b)), but I don't know what to look for (and if something like this even exists in MySQL). Is there anything like that, or I have to do something like this:
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
c1 = models.IntegerField()
c2 = models.IntegerField()
c3 = models.IntegerField()
class Meta:
unique_together = ('a', 'b')
-- and handle c1..c3 myself?
You should override the save() method for the model and check your constraint before each save and raise a ValueError if the constraint is violated.
class MyModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
c = models.IntegerField()
def save(self):
try:
# Check values in model here
except:
raise ValueError("Cannot save more than 3 Cs with an A")
super(MyModel, self).save(*args, **kwargs)