How to use values and distinct on different field on Django ORM? - python

currently I have terms on my applications and one user can have a lot of terms registered, and my current model is like this
class Term(models.Model):
id = models.UUIDField("id", default=uuid.uuid4, editable=False, primary_key=True)
user_id = models.PositiveIntegerField("user id", default=None)
name = models.CharField()
sometimes I need to do a query to get all the users who have terms registered, so I do the following query:
Term.objects.filter(active=True)
.order_by("user_id")
.values("user_id")
.distinct()
and this is enough to solve my problems, but now I'll change my model and it will look like this:
class Term(models.Model):
id = models.UUIDField("id", default=uuid.uuid4, editable=False, primary_key=True)
user_id = models.PositiveIntegerField("user id", default=None)
name = models.CharField()
shared_with = ArrayField(models.PositiveIntegerField("id do usuario"), blank=True) # New
How you can see, I've added a new field named shared_with, that basically is a array of user ids which I want to share terms, So now I need to make a query who will return all ids who can have terms registered (shared_with included). So if i register a Term with user_id = 1 and shared_with = [2,3], my query need to return [1,2,3].
I've solved this problem today with the following code, but I think I can do this just using django ORM and one query:
users = set()
for user in (
Term.objects.filter(active=True)
.order_by("user_id")
.values("user_id")
.distinct()
):
users.add(user["user_id"])
for user in (
Term.objects.filter(active=True)
.order_by("user_id")
.values("shared_with")
):
for user_id in user["shared_with"]:
users.add(user_id)
print(users) # {1,2,3}
If someone knows how to do it and can share the knowledge, I will be grateful.

I don't recommend using the PositiveIntegerField and ArrayField as relations between tables, you can use ForeignKey and ManyToManyField instead, in your case what I understand is a user can have many Terms and a Term can be shared among many users, so the perfect solution is to add ManyToManyField in your User model
class User(AbstarctUser):
... (your fields)
terms = models.ManyToManyField(Term, related_name="users")
and Term model will be like:
class Term(models.Model):
id = models.UUIDField("id", default=uuid.uuid4, editable=False, primary_key=True)
name = models.CharField(max_length=200)
active = models.BooleanField(default=True)
... (other fields)
in that case, if you want to extract user ids with active terms, you can get it as following :
users = User.objects.filter(terms__active=True).distinct().values_list("id", flat=True)

Related

New object not added to queryset in Django

Here is the model
class Student(models.Model):
"""Student info"""
id = models.CharField( max_length=7,primary_key=True)
name = models.CharField(_('name'),max_length=8, default=""); # help_text will locate after the field
address = models.CharField(_('address'),max_length=30,blank=True,default="") #blank true means the
GENDER_CHOICES = [("M", _("male")),("F",_("female"))]
student_number = models.CharField(max_length=10,blank=True)
gender = models.CharField(_("gender"),max_length=6, choices = GENDER_CHOICES, default="M");
I user shell to create two users as below:
But the queryset number didn't increase although I created two users.
#I hate Django. Q = Q
You are using a custom id field as model id and you put it as Char, so you should add this id also when you want to save an object
But it is not a good idea, if you want to use custom id use it in this way
id = models.AutoField(primary_key=True)

Django ORM multiple tables query

I trying to make some queries in Django ORM (migration from SQL). My models looks like this
class Iv2(models.Model):
s_id = models.AutoField(primary_key=True)
l_eid = models.CharField(max_length=265)
t_id = models.CharField(max_length=265,unique=True)
class Sv2(models.Model):
id = models.AutoField(primary_key=True)
s_id = models.OneToOneField(Iv2, on_delete=models.PROTECT)
gdd = models.DateTimeField(default=datetime.now)
class Ev2(models.Model):
id = models.OneToOneField(Iv2, to_field='l_eid', on_delete=models.PROTECT)
s_id = models.ForeignKey(Iv2, on_delete=models.PROTECT)
car = models.CharField(max_length=265)
I want to write a query, given t_id(some real search value). I want to get the corresponding Sv2.gdd and Ev2.car
I'm thinking to get s_id and l_eid with the t_id. And when I get s_id. I can query Sv2 and with l_eid I can query Ev2.
Is it possible to achieve everything with one ORM query ? can prefetch/select_related work here?
"Given t_id of Iv2 get Sv2.gdd and Ev2.car"
First get the Sv2 instance by filtering its 1:1 relation by the
real search value t_id:
sv2 = Sv2.objects.filter(s_id__t_id=t_id).first()
sv2.gdd
Now you have 2 options to get Ev2.car
Add related name in Ev2 (more on related_name here )
Assuming your related_name is ev2 you can do:
sv2.ev2.car
Use the default django related_name modelname__set
sv2.ev2_set

How can I add ordering for ManyToMany field?

I have two models: CustomUser and AgreementReglament
At this moment relation between the models looks like this:
class AgreementReglament(models.Model):
name = models.CharField(max_length=32) # just name of the reglament
approvers = models.ManyToManyField( # set of users to approve
'CustomUser',
related_name='agreement_reglaments'
)
How can I set order of approvers (e.g. CustomUser1 should be the first in particular AgreementReglament1, CustomUser2 should be second and so on, but in AgreementReglament2 CustomUser2 can be the first)
UPDATE: ordering is based on the POST request data
Example of post request to create AgreementReglament:
{
"name": "Reglament1",
"approvers": [
{
"approver_name": "Alex",
"approver_order": 1,
},
{
"approver_name": "Bob",
"approver_order": 2,
}
]
}
UPDATE2
class AgreementReglament(models.Model):
name = models.CharField(max_length=32) # name of the reglament
approvers = models.ManyToManyField( # set of users to approve
'CustomUser',
related_name='agreement_reglaments',
through='Approval'
)
class Approval(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
agreement = models.ForeignKey(
AgreementReglament,
on_delete=models.SET_NULL,
blank=True,
null=True
)
order = models.IntegerField()
If you mean the approver order value will be entered as a field , then I would create a model lets say For example Approvals and it will have a FK field for User and a FK field for Agreement , and a third field for the approver_order. And leave the Agreement Model to have only name field.
But , If you mean to sort based on a condition , the question needs more details to be clarified as (what the ordering is based on ? What is the field you’re ordering exactly ?)
UPDATE:
After seeing your update what I understood is that you get this ordering from the POST request data and for the example you provided, you definitely need a field to store the order for each User.
I suggest going with the same answer, create a model in the middle and link both models with that Model using the through attribute and that might look like this:
class Approval(models.Modal):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
agreement = models.ForeignKey(AgreementReglament, ...)
order = models.#whatever you want this char or int
and in one of the other models (doesn't really matter) they should be like this:
class AgreementReglament(models.Model):
name = models.CharField(max_length=32) # just name of the reglament
approvers = models.ManyToManyField( # set of users to approve
'CustomUser',
related_name='agreement_reglaments', through='Approval'
)
If you have any questions, feel free to ask.

Django ORM Get users not assigned to the Team

models.py
from django.db import models
class UserGroup(models.Model):
members = models.ManyToManyField(User, related_name='members', through='UserGroupMember')
class UserGroupMember(models.Model):
user = models.ForeignKey(User)
usergroup = models.ForeignKey(UserGroup)
class Cohort(models.Model):
user_groups = models.ManyToManyField(UserGroup)
class Team(models.Model):
cohort = models.ForeignKey(Cohort)
members = models.ManyToManyField(User, related_name='team_members', through='TeamMembers', blank=True)
class TeamMembers(models.Model):
team = models.ForeignKey(Team)
user = models.ForeignKey(User)
Single user can be part of only one team within a cohort.
Query #1
I need to write the query to get the users for a given cohort that are not assigned to any team.
I tried this:
User.objects.filter(
members__cohort=cohort
).filter(
team_members__isnull=True
)
but that does not give me the user if he is part of the team in another cohort.
Query #2
I want to annotate the new field (boolean) which tells you is the user assigned to some team in the cohort, something like:
User.objects.filter(
members__cohort=cohort
).annotate(
is_team_member=...
)
I am using Python 2.7.13 and Django 1.9.8. Thanks.
Try constructing the queryset by excluding all the members that are assigned to a team in the cohort:
User.objects.filter(
members_cohort=cohort,
).exclude(
team_members__cohort=cohort,
)

Django - storing logical tests as records

I'm working on a Gran Turismo 5 Django application. Here's a very simplified data model:
class Event(models.Model):
name = models.CharField(max_length=256, unique=True)
class EventCriteria(models.Model):
name = models.CharField(max_length=256, unique=True)
events = models.ManyToManyField(Event)
test = ???
class Country(models.Model):
name = models.CharField(max_length=256, unique=True)
class Make(models.Model):
name = models.CharField(max_length=256, unique=True)
country = models.ForeignKey(Country)
class Car(models.Model):
name = models.CharField(max_length=256, unique=True)
make = models.ForeignKey(Make)
class Setup(models.Model):
name = models.CharField(max_length=256, unique=True)
car = models.ForeignKey(Car)
horsepower = models.IntegerField()
For example, a given event might have the criteria 'Country = Italy'. When applied against the model above, that would require a test like the following:
setup.car.make.country.name == u'Italy'
Does anyone have a good framework for how I might structure the EventCriteria model (especially the 'test' field or fields') to make a) storing these tests and b) applying them as filters in future views possible?
Thanks,
Mike
It's not clear on why your "test" isn't a simple boolean field. The question is confusing.
I'm assuming that really want a persistent filter, since that's often requested.
A Django filter is a dictionary.
SomeModel.objects.filter( column=value, column__contains=value )
SomeModel.objects.filter( **{'column':value, 'column__contains':value} )
You can do this to persist your "test".
Convert your "filter" expression to a dictionary.
JSON-encode the dictionary as a BLOB
Save it.
You can apply your test as follows.
Get the filter BLOB
JSON-decode the dictionary
Use the dictionary in a filter for the appropriate class.

Categories