Modify prefetched objects - django

Is it possible to modify prefetched objects without using save()?
class First(models.Model):
field = models.IntegerField()
class Second(models.Model):
first = models.ForeignKey(First, related_name='seconds')
some = models.IntegerField()
qs = First.objects.filter(field=12).prefetch_related('seconds')
qs[0].seconds.all()[0].some = 999
qs[0].seconds.all()[0] == 999 # should be True
UPDATE:
I found what was wrong. First need evaluate queryset e.g. len(qs) after filter.
I want to alter my question slightly. Can i modify foreign key relationship without hitting db?
qs = First.objects.filter(field=12).prefetch_related('seconds')
len(qs)
print(len(qs[0].seconds.all())) # output: 5
qs[0].seconds.all()[0].first_id = 2
print(len(qs[0].seconds.all())) # output: 4

Instead of doing
f.seconds.all()[0].some = 999
f.seconds.all()[0] == 999 # should be True
Do this..
first_object = f.seconds.all()[0]
first_object.some = 999
first_object.some == 999 #true

Related

Rest django checking the duplicate number

if i inserting the number 5 i want to check all the numbers before 5( like 1,2,3 and 4 )is must have inside the db ,then only can i add the number 5 ,otherwise show error. And also check is any duplication in the Db using rest django model serilaizer.
def validate_match_round(self, match_round):
if not match_round :
raise serializers.ValidationError("Enter value more than 0 ")
matchscore model
class Matchscore(TimeStampedModel):
gameevent = models.ForeignKey(GameEvent, null=True, related_name='game_event',on_delete=models.DO_NOTHING)
match_round = models.IntegerField(null=True,blank=True)
team_a = models.ForeignKey(Team,null=True,related_name='team_one',on_delete=models.DO_NOTHING)
team_a_score = models.PositiveIntegerField(null=True,blank=True)
team_b = models.ForeignKey(Team,null=True,related_name='team_two',on_delete=models.DO_NOTHING)
team_b_score = models.PositiveIntegerField(null=True,blank=True)
team_won = models.ForeignKey(Team,null=True,related_name='team', on_delete=models.DO_NOTHING)
if match_round == 1:
return match_round
match = Matchscore.objects.aggregate(Max('match_round'))
print(match)
if match_round == match["match_round__max"] + 1 :
return match_round
else:
raise serializers.ValidationError("Enter a valid match_round")
all_rounds = Matchscore.objects.filter(match_round__lte=match_round).order_by('match_round').values_list('match_round', flat=True)
Will return list of match_round values that lower or equal than match_round passed to function
all_rounds_count = Matchscore.objects.filter(match_round__lte=match_round).count()
Will return count of these rounds. and if (all_rounds_count + 1) == match_round check is what you want
And also check is any duplication in the Db using rest django model serilaizer.
To avoid keys duplication
Set unique=True for model.Field to disable round duplicates match_round = models.IntegerField(..., unique = True)
Add unique validator to serializer

How to filter by range OR "null" value? (I.e. combine NumberFilter range and BooleanFilter for null=True IntegerField)

I have a Item model with a numeric number field. This number field defaults to null.
# models.py
class Item(models.Model):
number = models.IntegerField(default=None, blank=True, null=True)
I want to set-up filters that can return a queryset of Items where number is in range - which is straightforward enough:
# filters.py
class ItemFilter(django_filters.FilterSet):
min_num = django_filters.NumberFilter(method="min_num_filter")
max_num = django_filters.NumberFilter(method="max_num_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
def min_num_filter(self, queryset, name, value):
return queryset.filter(number__gte=value)
def max_num_filter(self, queryset, name, value):
return queryset.filter(number__lte=value)
But what if I want to have an additional Boolean filter that can include Items that has null for number along with whatever Items matches the min_num and max_num range?
So for example, a URL query in the form of ?min_num=1&max_num=10&incl_null=True should return all Items where number is between 1 and 10 OR number is equal to None.
The following code does not work:
class ItemFilter(django_filters.FilterSet):
...
incl_null = django_filters.BooleanFilter(method="incl_null_filter")
class Meta:
model = Item
fields = ("min_num", "max_num", "incl_null")
// doesn't work
class incl_null_filter(self, queryset, name, value):
if value is True:
return queryset | Item.objects.filter(number=None)
if value is False:
return queryset
Edit: I've tried the methods in the "Filtering by empty values" documentation but I think that's for null values exclusively - where I'm looking for a range match OR a null value.
Try this query:
from django.db.models import Q
min_ = 0
max_ = 10
Item.objects.filter(Q(number__gte=min_, number__lte=max_) | Q(number__isnull=True))
Well, the only solution I can think of is to pass the min range, max range, and is_null boolean into a single char field then convert it into the 3 individual filters for actioning.
So the query URL will look like ?master_num=1-10-1 for range 1 - 10 incl. None and ?master_num=1-10-0 for range 1 - 10 excl. None.
class ItemFilter(django_filters.FilterSet):
master_num = django_filters.CharFilter(method="master_num_filter")
class Meta:
model = Item
fields = ("master_num")
def master_num_filter(self, queryset, name, value):
# array = [min, max, 1 or 0 for True and False]
array = value.split("-")
min = Q(year_published__gte=int(array[0]))
max = Q(year_published__lte=int(array[1]))
if array[2] == "1":
incl_null = Q(year_published=None)
return queryset.filter((min & max) | incl_null)
else:
return queryset.filter(min & max)
Would like to know if there's a better way to do this.

Select values from two tables using Django

I have two tables as follows,
cabinet
cabinet_id cabinet_name
1 Test1
2 Test2
3 Test3
cabinet_maping
id product_id cabinet_id size
1 34 1 20
2 34 3 25
How can I select all cabinet_name from cabinet table where cabinet_maping.product_id = 34 in Django
Expected Output as follows,
cabinet_id cabinet_name size
1 Test1 20
2 Test2 0
3 Test3 25
I think that yours models could look like
class Cabinet(models.Model):
name = models.CharField(max_length = 30)
class cabinet_maping(models.Model):
product = models.ForeignKey(Product)
cabinet = models.ForeignKey(Cabinet)
size = .....
You should do sth like this:
cabinet_name = cabinet_maping.objects.filter(product__id = 34)
for item in cabinet_name:
print item.cabinet.id
print item.cabinet.name
print item.size
I didn't check it but i think that should work (that works :))
I agree with Silwestpl that your models are wrong as using Foreign Key would make this query a lot easier. But just an answer to your question would be. I am assuming that you dont have any relationships between the two tables as you haven't mentioned any.
x = Cabinet_mapping.objects.filter(product_id = somevalue).distinct('cabinet_id')
response_list = []
for y in x:
response_dict = {}
response_dict['cabinet_id'] = y.cabinet_id
response_dict['cabinet_name'] = cabinet.objects.get(id = y.cabinet_id).cabinet_name
response_dict['size'] = y.size
response_list.append(response_dict)
return response_list
This would be the answer according to the details you have provided.
But Ideally I would do something like this
class Product(models.Model):
# id is generated automatically.
## any other fields that you want for product.
class Cabinet(models.Model):
# again id is auto-genearated.
cabinet_name = models.CharField(max_length=100)
cabinet_size = models.IntegerField()
products = models.ManytoManyField(Product)#This automatically maps product_id n cabinet_id.
And your query would be.
x = Cabinet.objects.filter(product.id=some_value)
print (x.id , x.cabinet_name, x.cabinet_size) # I used print, but you can use it anyway you want.
This would be what you require. Please use the second solution if you are looking into something serious.

Django query using filters

I have 3 models in django:
class Movie(models.Model):
mid = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 100)
class User(models.Model):
username = models.CharField(max_length=128, null=True)
uid = models.CharField(max_length=100)
movie = models.ManyToManyField(Movie, through = "Vote")
class Vote(models.Model):
movie = models.ForeignKey(Movie)
user = models.ForeignKey(User)
rating = models.IntegerField()
here rating = 0/1, 0 means dislike, 1 means like
i want to make some queries using filters:
find out all movies that a current user likes. For that i use this following 2 queries, but none of them work. In both cases it gives erroneous results
ans = Movie.objects.filter(vote__user = self).filter(vote__rating = 1)
ans = Movie.objects.filter(user__uid = self.uid).filter(vote__rating = 1)
I have a list of users in an array ids. I want to find out how many users from this list like a particular movie?
i tried this, but this is also incorrect:
ret = User.objects.filter(uid__in = ids).filter(vote__movie = mov).filter(vote__rating = 1)
can somebody please help me with these 2 queries?
I'd also suggest letting django assign the model's id's but if you are using a legacy database or for some other reason need to assign the id's you can query like so:
# uid is some uid
user = User.objects.get(uid=uid)
likes = Movie.objects.filter(vote__user=user, vote__rating=1)
or
likes = Movie.objects.filter(vote__user__uid=some_uid, vote__rating=1)
count of people in the list of users who like a specific movie:
>>> uids = ['1','2','3']
>>> # if mov is a Movie instance
>>> votes = Vote.objects.filter(user__uid__in=uids, movie=mov, rating=1)
>>> print votes.query
SELECT "so1_vote"."id", "so1_vote"."movie_id", "so1_vote"."user_id", "so1_vote"."rating" FROM "so1_vote" INNER JOIN "so1_user" ON ("so1_vote"."user_id" = "so1_user"."id") WHERE ("so1_user"."uid" IN (1, 2, 3) AND "so1_vote"."movie_id" = 1 AND "so1_vote"."rating" = 1 )
>>> # if mov is a mid for a movie
>>> # get movie instance by using Movie.objects.get(mid=mov)
>>> # or query:
>>> # votes = Vote.objects.filter(user__uid__in=uids, movie__mid=mov, rating=1)
>>> likes_count = votes.count()
>>> print likes_count
0
combined:
likes_count = Votes.objects.filter(user__uid__in=uids, movie=mov, rating=1).count()

Django ORM equivalent for this SQL..calculated field derived from related table

I have the following model structure below:
class Master(models.Model):
name = models.CharField(max_length=50)
mounting_height = models.DecimalField(max_digits=10,decimal_places=2)
class MLog(models.Model):
date = models.DateField(db_index=True)
time = models.TimeField(db_index=True)
sensor_reading = models.IntegerField()
m_master = models.ForeignKey(Master)
The goal is to produce a queryset that returns all the fields from MLog plus a calculated field (item_height) based on the related data in Master
using Django's raw sql:
querySet = MLog.objects.raw('''
SELECT a.id,
date,
time,
sensor_reading,
mounting_height,
(sensor_reading - mounting_height) as item_height
FROM db_mlog a JOIN db_master b
ON a.m_master_id = b.id
''')
How do I code this using Django's ORM?
I can think of two ways to go about this without relying on raw(). The first is pretty much the same as what #tylerl suggested. Something like this:
class Master(models.Model):
name = models.CharField(max_length=50)
mounting_height = models.DecimalField(max_digits=10,decimal_places=2)
class MLog(models.Model):
date = models.DateField(db_index=True)
time = models.TimeField(db_index=True)
sensor_reading = models.IntegerField()
m_master = models.ForeignKey(Master)
def _get_item_height(self):
return self.sensor_reading - self.m_master.mounting_height
item_height = property(_get_item_height)
In this case I am defining a custom (derived) property for MLog called item_height. This property is calculated as the difference of the sensor_reading of an instance and the mounting_height of its related master instance. More on property here.
You can then do something like this:
In [4]: q = MLog.objects.all()
In [5]: q[0]
Out[5]: <MLog: 2010-09-11 8>
In [6]: q[0].item_height
Out[6]: Decimal('-2.00')
The second way to do this is to use the extra() method and have the database do the calculation for you.
In [14]: q = MLog.objects.select_related().extra(select =
{'item_height': 'sensor_reading - mounting_height'})
In [16]: q[0]
Out[16]: <MLog: 2010-09-11 8>
In [17]: q[0].item_height
Out[17]: Decimal('-2.00')
You'll note the use of select_related(). Without this the Master table will not be joined with the query and you will get an error.
I always do the calculations in the app rather than in the DB.
class Thing(models.Model):
foo = models.IntegerField()
bar = models.IntegerField()
#Property
def diff():
def fget(self):
return self.foo - self.bar
def fset(self,value):
self.bar = self.foo - value
Then you can manipulate it just as you would any other field, and it does whatever you defined with the underlying data. For example:
obj = Thing.objects.all()[0]
print(obj.diff) # prints .foo - .bar
obj.diff = 4 # sets .bar to .foo - 4
Property, by the way, is just a standard property decorator, in this case coded as follows (I don't remember where it came from):
def Property(function):
keys = 'fget', 'fset', 'fdel'
func_locals = {'doc':function.__doc__}
def probeFunc(frame, event, arg):
if event == 'return':
locals = frame.f_locals
func_locals.update(dict((k,locals.get(k)) for k in keys))
sys.settrace(None)
return probeFunc
sys.settrace(probeFunc)
function()
return property(**func_locals)