How do I override the results from an django API query - django

I'm very new to Django, so this might seem basic.
I have 2 models:
class Brand(models.Model):
brand_name = models.CharField()
class Foo(models.Model):
brand = models.ForeignKey(Brand, null=True)
foo_name = models.CharField()
Foo.brand can be null. Now when an API query is made on Foo, I want to include results where the brand's name matches but also where Foo.brand is null
If Foo looks like this:
Foo.brand, Foo.foo_name
1, "apple"
3, "orange"
1, "mango"
null, "grape"
/api/foo?brand=1 should return apple, mango and grape. How can I get this? I tried reading similar questions, it sounds like I need to override the viewset, but wasn't sure.
Thanks.

You can do it with override get_queryset method in your view class and use Q operator for creating or condition. You need to brand id is equal or null condition :
def get_queryset(self):
foos = Foo.objects.all()
brand_id = self.request.query_params.get('brand')
if brand_id:
foos = Foo.objects.filter(Q(brand_id__isnull=True)|Q(brand_id=brand_id))
return foos

Related

Django-orm Queryset for Find object by count a particular field

Let's say I have two models:
class Testmodel1():
amount = models.IntegerField(null=True)
contact = models.ForeignKey(Testmodel2)
entry_time = models.DateTimeField()
stage = choicesfiled
class Testmodel2():
name = models.CharField()
mobile_no = models.CharField()
I want to find out the object of Testmodel1 for contact > 3 which is created in the last 24 hours last = arrow.utcnow().shift(hours=-24).date().
I am applying a query:
n1=Testmodel1.objects.filter(entry_time__gte=last, stage=1).annotate(t_count=Count('contact')).filter(t_count__gt=3)
But it seems it's not working. Because I am getting an empty queryset.
Any help would be appreciated.
Only a partial answer. Sorry! Your code looks fine to me, so I'm just trying to find a solution by approaching it from a different direction.
Here's how I structure (sort of) similar code on one of my projects.
from datetime import timedelta, date
....
base_date = date.today()
start_date = base_date + timedelta(days=30)
end_date = base_date
possible_holidays = Holiday.objects.filter(
start_date__lte=start_date, end_date__gte=end_date)
From there, could you just do something like:
if possible_holidays.contact_set.count() > 3:
pass
Does that work?
The problem is your Many-to-One relationship is inverted. This relationship is a parent-child relationship, where a parent can have multiple children, but a children can only have one parent. In database this relationship is stored as a child's ForeignKey field that points to the child's parent.
In your case Testmodel1 is a parent and Testmodel2 is a child (Testmodel1 can have multiple contacts represented by Testmodel2) This means that ForeignKey field should belong to Testmodel2, not Testmodel1.
class Testmodel1():
amount = models.IntegerField(null=True)
entry_time = models.DateTimeField()
stage = choicesfiled
class Testmodel2():
name = models.CharField()
mobile_no = models.ForeignKey()
parent = models.ForeignKey(Testmodel1,
related_name='contacts',
)
With this model structure you can reference Testmodel1's contacts as testmodel1.contacts.all(). Your query then should look like this:
n1 = (Testmodel1.objects
.filter(entry_time__gte=last, stage=1)
.annotate(t_count=Count('contacts'))
.filter(t_count__gt=3)
)
docs reference

Filter objects which has no specific record met on its relational model

Supposed i have this model
class Employee(models.Model):
name = models.CharField(max_lenght=25)
class Attendance(models.Model):
employee = ForeignKey(Employee, on_delete=models.CASCADE)
is_present = models.BooleanField()
This is my implementation:
Employee.objects.bulk_create([
Employee(name='Greg'),
Employee(name='John'),
Employee(name='Lesley'),
])
emp1 = Employee.objects.get(name='Greg')
emp1.attendance_set.create(is_present=True)
emp1.attendance_set.create(is_present=True)
emp1.attendance_set.create(is_present=True)
emp2 = Employee.objects.get(name='John')
emp2.attendance_set.create(is_present=True)
emp2.attendance_set.create(is_present=True)
emp2.attendance_set.create(is_present=True)
emp3 = Employee.objects.get(name='Lesley')
emp3.attendance_set.create(is_present=True)
emp3.attendance_set.create(is_present=False)
Now how can i filter employees from Employee level which has no Attendance.is_present=False That will yield the distinct record of Greg and John?
I tried this so far but it includes Lesley.
Employee.objects.filter(attendance__is_present=True).distinct()
I was able to filter the right queryset using exclude()
First if you have some desired criteria of your query that needs to be include to the queryset you can do a filter() function, then do the exclude() function that will remove all Employee object who has record attendance__is_present=False
Employee.objects.filter(
# some criteria..
).exclude(
attendance__is_present=False
).distinct()
If there is no criteria apart from attendance__is_present=False you can do directly as what #Piyush S. Wanare answered.
Use:
Attendance.objects.filter(is_present=True)
You can directly do this:
Employee.objects.exclude(
attendance__is_present=False
).distinct()
Something like this should work -
employees = [attendance.employee for attendance in Attendance.objects.filter(is_present = False)]

How to make Q queries with models with foreign keys?

my model is defined like this:
class Model2(models.Model):
id = models.IntegerField(primary_key=True)
name = ...
class Model1(models.Model):
id = models.IntegerField(primary_key=True)
model2 = models.ForeignKey(Model2, to_field='id', db_column='model2')
The problem is I do not know how to make OR queries with queryset.
qs = Model1.objects.filter(Q(model2.id__icontains=search) | Q(id__icontains=search))
I get
keyword can't be an expression
So the question is how can I reference to the field of the related model in Q queries? Thanks
This should work:
qs = Model1.objects.filter(Q(model2__id=search) | Q(id=search))
I would revisit the implementation if you are looking for id__icontains
If you search for 1, then it would look for 1, 11, 121, 166..., (any id with 1 in it) which is probably not what you want.
To specifically point out your error,
Q(model2.id__icontains=search)
should be
Q(model2__id__icontains=search)

Django - Return JSON, Error

I got a View.py function that looks like this:
def GetAllCities(request):
cities = list(City.objects.all())
return HttpResponse(json.dumps(cities))
My City model looks like this
class City(models.Model):
city = models.CharField()
loc = models.CharField()
population = models.IntegerField()
state = models.CharField()
_id = models.CharField()
class MongoMeta:
db_table = "cities"
def __unicode__(self):
return self.city
I am using a MongoDB that looks like this
{
"_id" : ObjectId("5179837cbd7fe491c1f23227"),
"city" : "ACMAR",
"loc" : "[-86.51557, 33.584132]",
"state" : "AL",
"population" : 6055
}
I get the following error when trying to return the JSON from my GetAllCities function:
City ACMAR is not JSON serializable
So I tried this Instead:
def GetAllCities(request):
cities = serializers.serialize("json", City.objects.all())
return HttpResponse(cities)
And this works but It's very slow, it takes about 9 seconds(My database contains 30000 rows)
Should it take this long or am I doing something wrong?
I've built the same app in PHP, Rails and NodeJS.
In PHP it takes on average 2000ms, NodeJS = 800ms, Rails = 5882ms and Django 9395ms. Im trying to benchmark here so I wonder if there is a way to optimize my Django code or is this as fast as it gets?
For sure you do not need to return ALL cities, as you probably won't display all 30000 rows anyway (at least in user-friendly way). Consider a solution where you return only cities within some range from requested location. Mongo supports geospatial indexes, so there should be no problem in doing that. There are also many tutorials over the internet how to perform spatial filtering in Django/MongoDB.
def GetAllCities(request, lon, lat):
#Pseudo-code
cities = City.objects.filterWithingXkmFromLonLat(lon, lat).all()
cities = serializers.serialize("json", cities)
return HttpResponse(cities)
If you really, really need all cities, consider caching the response. Location, name and population of cities are not things which change dynamically, in a matter of let's say seconds. Cache the result and recalculate only every hour, day or more. Django supports cache out of the box
#cache_page(60 * 60)
def GetAllCities(request):
(...)
Another thing you can try to get a little more of speed is to get from db just the values you need and get the QuerySet to build the dictionary.
A simple query like this would work:
City.objects.all().values('id', 'city', 'loc', 'population', 'state')
Or you can put it in a manager:
class CitiesManager(models.Manager):
class as_dict(self):
return self.all().values('id', 'city', 'loc', 'population', 'state')
class City(models.Model):
.... your fields here...
objects = CitiesManager()
And then use it in your view as:
City.objects.as_dict()
FOUND A SOLUTION
I am benchmarking with different methods, one method is to see how fast one language/framework is to select ALL rows in a database and return it as JSON. I found a solution now that speeds it up by half the time!
My new views.py
def GetAllCities(request):
dictionaries = [ obj.as_dict() for obj in City.objects.all() ]
return HttpResponse(json.dumps({"Cities": dictionaries}), content_type='application/json')
And my new model
class City(models.Model):
city = models.CharField()
loc = models.CharField()
population = models.IntegerField()
state = models.CharField()
_id = models.CharField()
def as_dict(self):
return {
"id": self.id,
"city": self.city,
"loc": self.loc,
"population": self.population,
"state": self.state
# other stuff
}
class MongoMeta:
db_table = "cities"
def __unicode__(self):
return self.city
Found the solution here

Finding object count where a field is unique in Django

I have a model that is something like this:
class Input(models.Model):
details = models.CharField(max_length=1000)
user = models.ForeignKey(User)
class Case(Input):
title = models.CharField(max_length=200)
views = models.IntegerField()
class Argument(Input):
case = models.ForeignKey(Case)
side = models.BooleanField()
A user can submit many arguments, per case. I want to be able to say how many users have submitted side=true arguments.
I mean if 1 user had 10 arguments and another user had 2 arguments (both side=true)
I'd want the count to be 2, not 12.
Update:
I am using these methods on the Case object:
def users_agree(self):
return self.argument_set.filter(side=True).values('user').distinct()
def users_disagree(self):
return self.argument_set.filter(side=False).values('user').distinct()
My template code calls count() on them.
Can you try:
Argument.objects.filter(side=True).values('case__user').distinct().count()
I think it does what you want. It issues one SQL query:
SELECT COUNT(DISTINCT "example_input"."user_id") FROM "example_argument" INNER JOIN "example_case" ON ("example_argument"."case_id" = "example_case"."input_ptr_id") INNER JOIN "example_input" ON ("example_case"."input_ptr_id" = "example_input"."id") WHERE "example_argument"."side" = True
Edit:
For this_case, get all users whose argument.side is True:
Argument.objects.filter(case__id=this_case.id, side=True).values('user').distinct()