I want to create a query that creates a binary or combination of attributes.
For example, I have the following class
class foo(models.Model):
m_person= models.ForeignKey('Person', on_delete=models.CASCADE)
m_rang = models.ForeignKey('Rang', on_delete=models.CASCADE)
m_a = models.BooleanField(default=True, name='a')
m_b = models.BooleanField(default=False, name='b')
m_c = models.BooleanField(default=False, name='c')
m_d = models.BooleanField(default=False, name='d')
...
I am currently reading this class as follows:
obj = foo.objects.all().first()
attributes = [i for i in dir(obj) if i.startswith('m_')]
for tag in attributes:
if hasattr(foo, tag.__name__):
t = foo.objects.filter(person_id=aperson.pk, rang=arang).values_list(tag.__name__, flat=True)
if True in t:
do somthing
So I run through all the instances that match the person and the rank to then check the attribute.
I bet it's somehow more intelligent in combination.
I would actually like to read a combined "or" class from the database in order to process it further. instead of questioning each attribute individually...
since I need a or link, you could use first(value = true) somehow.
my wish would be something like this:
f = foo.objects.filter(person_id=aperson.pk, rang=arang).OR()
where the attributes then combine into something like this:
f.m_a = foo[0].m_a | foo[1].m_a | foo[2].m_a...
f.m_b = foo[0].m_b | foo[1].m_b | foo[2].m_b...
f.m_c = foo[0].m_c | foo[1].m_c | foo[2].m_c...
f.m_d = foo[0].m_d | foo[1].m_d | foo[2].m_d...
...
with:
foo[0] & foo[1] & foo[2] & ... element from person_id=aperson.pk and rang=arang
Related
I have models:
class Publisher(Model):
name = TextField()
class Author(Model):
name = TextField()
class Book(Model):
publisher = ForeignKey("Publisher")
author = ForeignKey("Author")
class Magazine(Model):
publisher = ForeignKey("Publisher")
writer = ForeignKey("Author")
I want to know which authors wrote for publishers. My version is this:
from django.db.models import TextField, F, Subquery, OuterRef
from django.contrib.postgres.aggregates import StringAgg # I use postgres
# to lead to the same name
books = Book.objects.annotate(author_name=F("author__name"))
magazines = Magazine.objects.annotate(author_name=F("writer__name"))
books = books.values("publisher_id", "author_name")
magazines = magazines.values("publisher_id", "author_name")
product = books.union(magazines)
# !! here I have a problem with grouping
product = product.group_by(
"publisher_id"
).annonate(
author_names=StringAgg("author_name", ";")
)
publishers = Publisher.objects.all().annotate(
author_names=Subquery(
product.filter(publisher_id=OuterRef("id")).values("author_names")[:1],
output_field=TextField()
)
)
# I was expecting something like
# name | author_names
# ------------------------------------------
# Publisher1 | Author1;Author2;Author3
# Publisher2 | Author2
# Publisher3 | Author2;Author3
The problem is that QuerySet has no .group_by() method, instead the .values() method is suggested (product.values("publisher_id").annonate(...)).
But this is complicated by the fact that I had previously called .values("publisher_id", "author_name") to bring two different models into the same view.
I also tried using .only("publisher_id", "author_name"), but (maybe it's a Django bug) this method can't work together with annotated and normal fields.
Is there any way to fix this problem or some other way to get a list of authors for a publisher?
Here is the db looks like:
id | Post | tag
1 | Post(1) | 'a'
2 | Post(1) | 'b'
3 | Post(2) | 'a'
4 | Post(3) | 'b'
And here is the code of the module
class PostMention(models.Model):
tag = models.CharField(max_length=200)
post = models.ForeignKey(Post,on_delete=models.CASCADE)
Here is the code of search,
def findPostTag(tag):
keywords=tag.split(' ')
keyQs = [Q(tag=x) for x in keywords]
keyQ = keyQs.pop()
for i in keyQs:
keyQ &= i
a = PostMention.objects.filter(keyQ).order_by('-id')
if not a:
a=[]
return a
(this code does not work correctly)
I withdraw the tags and save each as one row in the database. Now I want to make a search function that the user can input more than one keywords at the same time, like 'a b', and it will return 'Post(1)'. I searched for some similar situations, but seems all about searching for multi keywords in one row at the same time, like using Q(tag='a') & Q(tag='b'), it will search for the tag that equals to both 'a' and 'b'(in my view), which is not what I want (and get no result, obviously). So is there any solution to solve this? Thanks.
Is this cases, django provides, ManyToManyField, to work correctly you must to use:
class Tags(models.Model):
tag = models.CharField(unique=True, verbose_name='Tags')
class Post(models.Model): #your model
title = models.CharField(verbosone_name = 'Title')
post_tags = models.ManyToManyField(Tags, verbose_name='Choice your tags')
So you'll choice many tags to your post
Is there a more "Python/Django" way to query/filter objects by generic foreign key? I'm trying to get all FullCitation objects for a particular software, where is_primary is True.
I know I can't do this but I want to do something like this:
ct_supported = ContentType.objects.get(app_label="supportedprogram", model="software")
primary_citations = FullCitation.objects.filter(content_type__name=ct_supported, object_id__in='', is_primary=True)
models.py
class FullCitation(models.Model)
# the software to which this citation belongs
# either a supported software program or a non-supported software program
limit = models.Q(app_label = 'myprograms', model = 'supportedprogram') | models.Q(app_label = 'myprograms', model = 'nonsupportedprogram')
content_type = models.ForeignKey(ContentType), limit_choices_to = limit, )
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
is_primary = models.BooleanField(help_text="Is this the Primary Citation for the software program?")
class NonSupportedProgram(models.Model):
title = models.CharField(max_length=256, blank = True)
full_citation = generic.GenericRelation('FullCitation')
class SupportedProgram(models.Model):
title = models.CharField(max_length=256, blank = True)
full_citation = generic.GenericRelation('FullCitation')
# and a bunch of other fields.....
views.py # My current attempt
primary_citations = []
sw_citations = sw.full_citations.all()
for x in sw_citations:
if x.is_primary:
primary_citations.append(x)
Comprehensions should be a last resort for filtering QuerySets. Far better to let them remain as QuerySets as long as you can. I think this is what you're looking for:
ct_supported = ContentType.objects.get_for_model(SupportedProgram))
primary_citations = FullCitation.objects.filter(content_type=ct_supported, is_primary=True)
Updated:
If you want to filter for a specific SupportedProgram instance, do this:
my_supported = SupportedProgram.objects.get(id=instance_id_goes_here)
ct_supported = ContentType.objects.get_for_model(SupportedProgram))
primary_citations = FullCitation.objects.filter(content_object=my_supported, content_type=ct_supported, is_primary=True)
I have a model that looks like this:
class WeekOne(models.Model):
# Required benchmarks for given exercises
squatBenchmark = 1000
lungeBenchmark = 250
stairDaysCountBenchmark = 3
totalGoals = 4
squats = models.PositiveIntegerField(default=0)
lunges = models.PositiveIntegerField(default=0)
skipStairs = models.BooleanField(default=False)
stairDaysCount = models.PositiveSmallIntegerField(default=0)
# Running count of benchmarks met.
completeCount = models.PositiveSmallIntegerField(default=0)
# Set to true if benchmarks reached.
weekOneComplete = models.BooleanField(default=False)
I want to access the field 'squats', i.e., in a variable assignment amount = user.week_one.squats, but because of the way the views and templates work, I don't have access to a reference to the squats field, I only have a string squats. Is there any way to use this string to access that field?
This is what getattr is for:
amount = getattr(user.week_one, 'squats')
I am working on a catalog of classified grouped by categories.
However when I submit my form, I get the following error message:
Caught ValueError while rendering: Cannot assign "u'9'": "Classified.category" must be a "Category" instance.
I believe this is because Django expects a Category objects instead of a simple integer which corresponds to the chosen Category ID.
Here is how I wrote the system:
A classified is linked to one category.
The category system is hierarchical with a parent category and a list of child categories.
For example I have something like this:
Electronics
|-- IPad
|-- IPods
|-- ...
So I have the following models:
class Category(BaseModel):
# [...]
name = models.CharField(u'Name', max_length=50)
slug = AutoSlugField(populate_from='name', slugify=slugify, unique=True,
unique_with='name', max_length=255, default='')
parent = models.IntegerField(u'parent', max_length=10, null=False,
default=0)
objects = CategoryManager()
# [...]
class Classified(BaseModel):
# [...]
category = models.ForeignKey(Category, related_name='classifieds')
I created the following manager:
class CategoryManager(Manager):
def categoryTree(self):
tree = self.raw("SELECT"
" P.id, P.name parent_name, P.slug parent_slug, P.id parent_id,"
" C.name child_name, C.slug child_slug, C.id child_id"
" FROM classified_category C"
" LEFT JOIN classified_category P ON P.id = C.parent"
" WHERE C.parent <> 0"
" ORDER BY P.name, C.name;")
categoryTree = []
current_parent_id = tree[0].parent_id
current_parent_name = tree[0].parent_name
option_list = []
for c in tree:
if current_parent_id != c.parent_id:
categoryTree.append((current_parent_name, option_list))
option_list = []
current_parent_id = c.parent_id
current_parent_name = c.parent_name
option_list.append((c.child_id, c.child_name))
categoryTree.append((current_parent_name, option_list))
return category
And my Django form contains the following:
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
# [...]
If I use category = forms.ModelChoiceField(Category.objects.all()) everything works fine but I need to control how the <select> field is displayed with a list of <optgroup>. This is why use categoryTree()
But unfortunately using CategoryManager.categoryTree() breaks my form validation and I do not know how to fix my problem.
If I could be pointed to where I was wrong and how I can fix this, that would be awesome.
Thanks in advance for your help.
Quick solution is to save category manually
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
class Meta:
exclude=('category',)
def save(self):
classified = super(ClassifiedForm, self).save(commit=False)
classified.category = Category.objects.get(id=self.cleaned_data['category'])
classified.save()
return classified
You can and should still use a ModelChoiceField. The list of choices can be modified in the init method of the form class - i.e.
class ClassifiedForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ClassifiedForm, self).__init__(*args, **kwargs)
# Set the queryset for validation purposes.
# May not be necessary if categoryTree contains all categories
self.fields['category'].queryset = Category.objects.categoryTreeObjects()
# Set the choices
self.fields['category'].choices = Category.objects.categoryTree()
Also, you should look carefully at the django-mptt package. It looks like you may be reinventing the wheel here.