I am new to django so apologies if this is not possible or easy.
I have a view that takes a subset of a model
data = Terms.objects.filter(language = language_id)
The subset is one language. The set has a number of concepts for a language. Some languages might use the same word for multiple concepts, and I want to colour these the same in an SVG image. So I do this next:
for d in data:
if d.term is None:
d.colour = "#D3D3D3"
else:
d.colour = termColours[d.term]
Where termColours is a dictionary with keys as the unique terms and values as the hexadecimal colour I want.
I thought this would add a new colour attribute to my queryset. However, when I convert the queryset to json (in order to pass it to JS) the colour object is not there.
terms_json = serializers.serialize('json', data)
How can I add a new colour element to my queryset?
Convert your Queryset to Dict and then modify values.
Ex:
data = Terms.objects.filter(language = language_id).values()
for d in data:
if d.term is None:
d.colour = "#D3D3D3"
else:
d.colour = termColours[d.term]
If I understand correctly - you need Django ORM annotation. And it might look like that:
from django.db.models import Case, When, Value
data = Terms.objects.filter(language = language_id)
.annotate(color = Case(
When(term__isnull = True, then = "#D3D3D3"),
When(term__isnull = False, then = termColours[Value(term)]),))
Only problem here - I don't exactly know this moment - termColours[Value(term)], you need to test different combinations of that expressions to get the value of field term.
Related
date_one = form.cleaned_data.get('date_one')
date_two = form.cleaned_data.get('date_two')
date_type = form.cleaned_data.get('date_type')
search = MyClass.objects.filter(date_type__range(date_one, date_two))
My model has two different date columns. (created and expires). The user can make a query filtering between two dates, but he can choose if he wants to filter by creation or expiration.
I could make two query lines using if, but I really want to know how to do it in the way I'm asking.
How can I do this? Since the key before __range is a variable. I tried with (**{ filter: search_string }) but it seems not to be compatible with __range.
try this
filter_dict = {"{}__range".format(date_type): [date_one, date_two]}
search = MyClass.objects.filter(**filter_dict)
The thing you attempted is almost correct!
Lookups are not functions (so it's not foo__range(start, end)), but they are keyword arguments: foo__range=(start, end)
So you would have:
date_one = form.cleaned_data.get('date_one')
date_two = form.cleaned_data.get('date_two')
date_type = form.cleaned_data.get('date_type')
query_kwargs = {
"{}__range".format(date_type): (date_one, date_two)
}
search = MyClass.objects.filter(**query_kwargs)
I have the following models:
class Member(models.Model):
ref = models.CharField(max_length=200)
# some other stuff
def __str__(self):
return self.ref
class Feature(models.Model):
feature_id = models.BigIntegerField(default=0)
members = models.ManyToManyField(Member)
# some other stuff
A Member is basically just a pointer to a Feature. So let's say I have Features:
feature_id = 2, members = 1, 2
feature_id = 4
feature_id = 3
Then the members would be:
id = 1, ref = 4
id = 2, ref = 3
I want to find all of the Features which contain one or more Members from a list of "ok members." Currently my query looks like this:
# ndtmp is a query set of member-less Features which Members can point to
sids = [str(i) for i in list(ndtmp.values('feature_id'))]
# now make a query set that contains all rels and ways with at least one member with an id in sids
okmems = Member.objects.filter(ref__in=sids)
relsways = Feature.geoobjects.filter(members__in=okmems)
# now combine with nodes
op = relsways | ndtmp
This is enormously slow, and I'm not even sure if it's working. I've tried using print statements to debug, just to make sure anything is actually being parsed, and I get the following:
print(ndtmp.count())
>>> 12747
print(len(sids))
>>> 12747
print(okmems.count())
... and then the code just hangs for minutes, and eventually I quit it. I think that I just overcomplicated the query, but I'm not sure how best to simplify it. Should I:
Migrate Feature to use a CharField instead of a BigIntegerField? There is no real reason for me to use a BigIntegerField, I just did so because I was following a tutorial when I began this project. I tried a simple migration by just changing it in models.py and I got a "numeric" value in the column in PostgreSQL with format 'Decimal:( the id )', but there's probably some way around that that would force it to just shove the id into a string.
Use some feature of Many-To-Many Fields which I don't know abut to more efficiently check for matches
Calculate the bounding box of each Feature and store it in another column so that I don't have to do this calculation every time I query the database (so just the single fixed cost of calculation upon Migration + the cost of calculating whenever I add a new Feature or modify an existing one)?
Or something else? In case it helps, this is for a server-side script for an ongoing OpenStreetMap related project of mine, and you can see the work in progress here.
EDIT - I think a much faster way to get ndids is like this:
ndids = ndtmp.values_list('feature_id', flat=True)
This works, producing a non-empty set of ids.
Unfortunately, I am still at a loss as to how to get okmems. I tried:
okmems = Member.objects.filter(ref__in=str(ndids))
But it returns an empty query set. And I can confirm that the ref points are correct, via the following test:
Member.objects.values('ref')[:1]
>>> [{'ref': '2286047272'}]
Feature.objects.filter(feature_id='2286047272').values('feature_id')[:1]
>>> [{'feature_id': '2286047272'}]
You should take a look at annotate:
okmems = Member.objects.annotate(
feat_count=models.Count('feature')).filter(feat_count__gte=1)
relsways = Feature.geoobjects.filter(members__in=okmems)
Ultimately, I was wrong to set up the database using a numeric id in one table and a text-type id in the other. I am not very familiar with migrations yet, but as some point I'll have to take a deep dive into that world and figure out how to migrate my database to use numerics on both. For now, this works:
# ndtmp is a query set of member-less Features which Members can point to
# get the unique ids from ndtmp as strings
strids = ndtmp.extra({'feature_id_str':"CAST( \
feature_id AS VARCHAR)"}).order_by( \
'-feature_id_str').values_list('feature_id_str',flat=True).distinct()
# find all members whose ref values can be found in stride
okmems = Member.objects.filter(ref__in=strids)
# find all features containing one or more members in the accepted members list
relsways = Feature.geoobjects.filter(members__in=okmems)
# combine that with my existing list of allowed member-less features
op = relsways | ndtmp
# prove that this set is not empty
op.count()
# takes about 10 seconds
>>> 8997148 # looks like it worked!
Basically, I am making a query set of feature_ids (numerics) and casting it to be a query set of text-type (varchar) field values. I am then using values_list to make it only contain these string id values, and then I am finding all of the members whose ref ids are in that list of allowed Features. Now I know which members are allowed, so I can filter out all the Features which contain one or more members in that allowed list. Finally, I combine this query set of allowed Features which contain members with ndtmp, my original query set of allowed Features which do not contain members.
I have a view in Django that will read a get request and its parameters and make a query based on the parameters. Currently my view looks like this:
def getInventory(request):
c = request.GET.get('category', '')
g = request.GET.get('gender', '')
s = request.GET.get('size', '')
available = Item.objects.filter(gender=g,category=c,size=s)
data = serializers.serialize('json',available)
return HttpResponse(data,'json')
Sometimes, though, one of the parameters is not specified. I would like this to result in a value representing 'all' rather than an individual value. Is this possible in the way I've done it? I've tried gender=None but that just results in an empty list.
You need to apply a filter only if you want to filter something. You could do something like this:
# Start off with a base queryset
available = Item.objects.all()
allowed_filters = ['category', 'gender', 'size']
for f in allowed_filters:
if request.GET.get(f):
available = available.filter(**{f: request.GET[f]})
data = serializers.serialize('json',available)
return HttpResponse(data,'json')
That said, you might want to consider using the Forms API to validate the inputs before passing them to your queryset.
I've got an abstract ProductAttribute model that can be associated with a Product model. The attributes can be accessed using product.attribute_values.
Say I've got a form which contains two select boxes that allow the user to specify the values of two attributes - Size and Weight.
How do I, in a single query, pass in those values into a filter, so that - as you can with a list of integers with Object.objects.filter(pk__in=(1,2,3)) - I can select the Product that matches against all those attribute values?
I'd like to be able to do something like:
options = ['XL','50lbs']
p = Product.objects.filter(attribute_values__matches=options)
Is this possible in a one-liner in Django?
TIA
You can use __contains or __exact
and for case insensitive matches __icontains and __iexact
You can use Q model lookups to do an and filtering:
options = ['X1', 'X2', 'X3']
qs = [Q(attribute_name=option) for option in options] #or attribute_name__icontains - or whatever
query = qs.pop() #get the first element
for q in qs:
query |= q
qs = MyModel.objects.filter(query)
I ended up cracking this one with a little help from #karthikr above, and the the Q.add() method:
options = {'Size':'XL'}
qs = Q()
for key, val in options.iteritems():
qs.add(Q(attributes__name=key) & Q(attribute_values__value_option__option=val), qs.connector)
Hope this helps someone else in some small way in future.
I have a multilingual field which i used hvad package.
I have a script like below, which i use for the tastypie dehydration.
array = []
for t in bundle.obj.facilities.filter(foo_type = i.foo_type):
for field in get_translatable_fields(t.foo_type.__class__):
for translation in t.foo_type.translations.all():
value = getattr(translation, field)
array.append(value)
print array
But i get all the language translations in the same list. Do you have any idea to have different lists belong to different languages.
I just want to have different arrays that differentiate during for translation in .... iteration
You can store them in a dictionary, indexed by the translation, using a collections.defaultdict:
import collections
dict_all = collections.defaultdict(list)
for t in bundle.obj.facilities.filter(foo_type = i.foo_type):
for field in get_translatable_fields(t.foo_type.__class__):
for translation in t.foo_type.translations.all():
value = getattr(translation, field)
dict_all[translation.language_code].append(value)
If you'd like to turn it back into a regular dictionary afterwards (instead of a defaultdict):
dict_all = dict(dict_all.items())