I have the following model:
class P (models.Model):
title = models.CharField(max_length=100)
votes_up = models.ManyToManyField(User)
votes_down = models.ManyToManyField(User)
Is there a way to do something like
P.objects.annotate(rating = Count(votes_up) - Count(votes_down)).order_by('-rating')
?
Nope.
You either write your own aggregate function and then use it in your annotation (which is really a bad idea) or simply rely on raw sql.
Personally, I'd handle that use case with a denormalized counter, though.
Why are you using m2m relations to represent votes? Maybe change that to integerfields instead. Then you could have a model method that returns the subtracted amount. And then maybe you could annotate on the model method instead.
Related
I have a table called Clue which has a foreignkey relation with another entity called Entry.
class Entry(models.Model):
entry_text = models.CharField(max_length=50, unique=True)
.....
class Clue(models.Model):
entry = models.ForeignKey(Entry, on_delete=models.CASCADE)
......
Now, let's say I have the following queryset
clues = Clue.objects.filter(clue_text=clue.clue_text)
which returns something like this-
[<Clue: ATREST-Still>, <Clue: ATREST-Still>, <Clue: ATREST-Still>, <Clue: YET-Still>, <Clue: YET-Still>, <Clue: SILENT-Still>]
As, you can see there are different clue objects but some of them are tied to the same entry objects.
I tried the following:-
clues = Clue.objects.filter(clue_text=clue.clue_text).distinct()
But this won't work as the field repeating is a foreign key value. Correct me if I am wrong.
Essentially, I want my queryset to look something like this
[<Clue: ATREST-Still>, <Clue: YET-Still>, <Clue: SILENT-Still>]
I was able to achieve it through the following but I was looking at a solution that can be done at the database level rather than doing it in memory.
This is my approach
clue_objs=[]
temp = {}
clues = Clue.objects.filter(clue_text=clue.clue_text)
for clue in clues:
if not temp.get(clue.entry.entry_text):
temp[clue.entry.entry_text]=1
clue_objs.append(clue)
You can call .distinct() on the QuerySet you obtain with .values_list(…) [Django-doc], so something like:
clues = Clue.objects.filter(
clue_text=clue.clue_text
).values_list('clue_text', flat=True).distinct()
But this looks more like a modeling problem: if you have a lot of duplicated data, that often means you should construct a new model that stores that data only once, and then reference that model with a relation (like a ForeignKey, OneToOneField or ManyToManyField).
I have this models:
A = class(models.Model):
onefield = ...
B = class(models.Model):
property = models.CharField(...)
foreign = models.ForeignKey(A)
So, I want to get all the objects A that are ForeignKeys of objects B with property = x.
I tried this:
query = B.objects.filter(property=x).select_related('A')
But it doesn't work. Is it possible to do this with select_related()?
Although I hesitate to contradict the illustrious Alex, and he's technically correct (the best kind of correct, after all), select_related is not the answer here. Using that never gives you different objects as a result; it only improves the efficiency of subsequently accessing related objects.
To get As, you need to start your query from A. You can use the double-underscore syntax to filter on the related property. So:
query = A.objects.filter(b__property=x)
You need to write .select_related('foreign'). select_related takes a field name, not a class name.
Suppose I have something like this in my models.py:
class Hipster(models.Model):
name = CharField(max_length=50)
class Party(models.Model):
organiser = models.ForeignKey()
participants = models.ManyToManyField(Profile, related_name="participants")
Now in my views.py I would like to do a query which would fetch a party for the user where there are more than 0 participants.
Something like this maybe:
user = Hipster.get(pk=1)
hip_parties = Party.objects.filter(organiser=user, len(participants) > 0)
What's the best way of doing it?
If this works this is how I would do it.
Best way can mean a lot of things: best performance, most maintainable, etc. Therefore I will not say this is the best way, but I like to stick to the ORM features as much as possible since it seems more maintainable.
from django.db.models import Count
user = Hipster.objects.get(pk=1)
hip_parties = (Party.objects.annotate(num_participants=Count('participants'))
.filter(organiser=user, num_participants__gt=0))
Party.objects.filter(organizer=user, participants__isnull=False)
Party.objects.filter(organizer=user, participants=None)
Easier with exclude:
# organized by user and has more than 0 participants
Party.objects.filter(organizer=user).exclude(participants=None)
Also returns distinct results
Derived from #Yuji-'Tomita'-Tomita answer, I've also added .distinct('id') to exclude the duplitate records:
Party.objects.filter(organizer=user, participants__isnull=False).distinct('id')
Therefore, each party is listed only once.
I use the following method when trying to return a queryset having at least one object in a manytomany field:
First, return all the possible manytomany objects:
profiles = Profile.objects.all()
Next, filter the model by returning only the queryset containing at least one of the profiles:
hid_parties = Party.objects.filter(profiles__in=profiles)
To do the above in a single line:
hid_parties = Party.objects.filter(profiles__in=Profile.objects.all())
You can further refine individual querysets the normal way for more specific filtering.
NOTE:This may not be the most effective way, but at least it works for me.
Below is a stripped down model and associated method. I am looking for a simple way upon executing a query to get all of the needed information in a single answer without having to re-query everything. The challenge here is the value is dependent upon the signedness of value_id.
class Property(models.Model):
property_definition = models.ForeignKey(PropertyDefinition)
owner = models.IntegerField()
value_id = models.IntegerField()
def get_value(self):
if self.value_id < 0: return PropertyLong.objects.get(id=-self.value_id)
else: return PropertyShort.objects.get(id=self.value_id)
Right now to get the "value" I need to do this:
object = Property.objects.get(property_definition__name="foo")
print object.get_value()
Can someone provide a cleaner way to solve this or is it "good" enough? Ideally I would like to simply just do this.
object = Property.objects.get(property_definition__name="foo")
object.value
Thanks
Given this is a bad design. You can use the builtin property decorator for your method to make it act as a property.
class Property(models.Model):
property_definition = models.ForeignKey(PropertyDefinition)
owner = models.IntegerField()
value_id = models.IntegerField()
#property
def value(self):
if self.value_id < 0: return PropertyLong.objects.get(id=-self.value_id)
else: return PropertyShort.objects.get(id=self.value_id)
This would enable you to do what you'd ideally like to do: Property.objects.get(pk=1).value
But I would go as far as to call this "cleaner". ;-)
You could go further and write your own custom model field by extending django.models.Field to hide the nastiness in your schema behind an API. This would at least give you the API you want now, so you can migrate the nastiness out later.
That or the Generic Keys mentioned by others. Choose your poison...
this is a bad design. as Daniel Roseman said, take a look at generic foreign keys if you must reference two different models from the same field.
https://docs.djangoproject.com/en/1.3/ref/contrib/contenttypes/#generic-relations
Model inheritance could be used since value is not a Field instance.
this is a model of the view table.
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
this is the SQL i use to create the table
CREATE VIEW qry_desc_char as
SELECT
tbl_desc.iid_id,
tbl_desc.cid_id,
tbl_desc.cs,
tbl_char.cid,
tbl_char.charname
FROM tbl_desC,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
i dont know if i need a function in models or views or both. i want to get a list of objects from that database to display it. This might be easy but im new at Django and python so i having some problems
Django 1.1 brought in a new feature that you might find useful. You should be able to do something like:
class QryDescChar(models.Model):
iid_id = models.IntegerField()
cid_id = models.IntegerField()
cs = models.CharField(max_length=10)
cid = models.IntegerField()
charname = models.CharField(max_length=50)
class Meta:
db_table = u'qry_desc_char'
managed = False
The documentation for the managed Meta class option is here. A relevant quote:
If False, no database table creation
or deletion operations will be
performed for this model. This is
useful if the model represents an
existing table or a database view that
has been created by some other means.
This is the only difference when
managed is False. All other aspects of
model handling are exactly the same as
normal.
Once that is done, you should be able to use your model normally. To get a list of objects you'd do something like:
qry_desc_char_list = QryDescChar.objects.all()
To actually get the list into your template you might want to look at generic views, specifically the object_list view.
If your RDBMS lets you create writable views and the view you create has the exact structure than the table Django would create I guess that should work directly.
(This is an old question, but is an area that still trips people up and is still highly relevant to anyone using Django with a pre-existing, normalized schema.)
In your SELECT statement you will need to add a numeric "id" because Django expects one, even on an unmanaged model. You can use the row_number() window function to accomplish this if there isn't a guaranteed unique integer value on the row somewhere (and with views this is often the case).
In this case I'm using an ORDER BY clause with the window function, but you can do anything that's valid, and while you're at it you may as well use a clause that's useful to you in some way. Just make sure you do not try to use Django ORM dot references to relations because they look for the "id" column by default, and yours are fake.
Additionally I would consider renaming my output columns to something more meaningful if you're going to use it within an object. With those changes in place the query would look more like (of course, substitute your own terms for the "AS" clauses):
CREATE VIEW qry_desc_char as
SELECT
row_number() OVER (ORDER BY tbl_char.cid) AS id,
tbl_desc.iid_id AS iid_id,
tbl_desc.cid_id AS cid_id,
tbl_desc.cs AS a_better_name,
tbl_char.cid AS something_descriptive,
tbl_char.charname AS name
FROM tbl_desc,tbl_char
WHERE tbl_desc.cid_id = tbl_char.cid;
Once that is done, in Django your model could look like this:
class QryDescChar(models.Model):
iid_id = models.ForeignKey('WhateverIidIs', related_name='+',
db_column='iid_id', on_delete=models.DO_NOTHING)
cid_id = models.ForeignKey('WhateverCidIs', related_name='+',
db_column='cid_id', on_delete=models.DO_NOTHING)
a_better_name = models.CharField(max_length=10)
something_descriptive = models.IntegerField()
name = models.CharField(max_length=50)
class Meta:
managed = False
db_table = 'qry_desc_char'
You don't need the "_id" part on the end of the id column names, because you can declare the column name on the Django model with something more descriptive using the "db_column" argument as I did above (but here I only it to prevent Django from adding another "_id" to the end of cid_id and iid_id -- which added zero semantic value to your code). Also, note the "on_delete" argument. Django does its own thing when it comes to cascading deletes, and on an interesting data model you don't want this -- and when it comes to views you'll just get an error and an aborted transaction. Prior to Django 1.5 you have to patch it to make DO_NOTHING actually mean "do nothing" -- otherwise it will still try to (needlessly) query and collect all related objects before going through its delete cycle, and the query will fail, halting the entire operation.
Incidentally, I wrote an in-depth explanation of how to do this just the other day.
You are trying to fetch records from a view. This is not correct as a view does not map to a model, a table maps to a model.
You should use Django ORM to fetch QryDescChar objects. Please note that Django ORM will fetch them directly from the table. You can consult Django docs for extra() and select_related() methods which will allow you to fetch related data (data you want to get from the other table) in different ways.