I have in models.py:
class Game(models.Model):
players1 = models.ManyToManyField(Player, related_name='games1')
players2 = models.ManyToManyField(Player, related_name='games2')
def get_all_players(self):
return list(itertools.chain(self.players1.all(), self.players2.all()))
How can I write same get_all_players method, but return QuerySet, not list?
P.S. I know that there is | operator:
def get_all_players(self):
return self.players1.all() | self.players2.all()
But it works in a very strange way. Result of this function contains more players than there are in players1 + players2 (result contains repeats of some players)
For a perhaps more semantically clear solution:
def get_all_players(self):
return (self.players1.all() | self.players2.all()).distinct()
This should do the trick:
# On the top of the file:
from django.db.models import Q
# Game instance method:
def get_all_players(self):
return Player.objects.filter(Q(games1__pk=self.pk) | Q(games2__pk=self.pk))
Q is described in details here: Complex lookups with Q objects.
Related
I'm new to python REST API and so facing some particular problems. I want that when I enter the input as pathlabid(primary key), I want the corresponding data assigned with that key as output. When I run the following code i only get the data corresponding to the first row of table in database even when the id i enter belong to some other row.
This is the VIEWS.PY
class pathlabAPI(View):
#csrf_exempt
def dispatch(self, *args, **kwargs):
# dont worry about the CSRF here
return super(pathlabAPI, self).dispatch(*args, **kwargs)
def post(self, request):
post_data = json.loads(request.body)
Pathlabid = post_data.get('Pathlabid') or ''
lablist = []
labdict = {}
lab = pathlab()
labs = lab.apply_filter(Pathlabid = Pathlabid)
if Pathlabid :
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return HttpResponse(json.dumps(lablist))
There are a number of issues with the overall approach and code but to fix the issue you're describing, but as a first fix I agree with the other answer: you need to take the return statement out of the loop. Right now you're returning your list as soon as you step through the loop one time, which is why you always get a list with one element. Here's a fix for that (you will need to add from django.http import JsonResponse at the top of your code):
if Pathlabid:
for p in labs:
labdict["Pathlabid"] = p.Pathlabid
labdict["name"] = p.Name
labdict["email_id"] = p.Emailid
labdict["contact_no"] = p.BasicContact
labdict["alternate_contact_no"] = p.AlternateContact
labdict["bank_account_number"] = p.Accountnumber
labdict["ifsccode"] = p.IFSCcode
labdict["country"] = p.Country
labdict["homepickup"] = p.Homepickup
lablist.append(labdict)
else:
for p in labs:
labdict["bank_account_number"] = p.Accountnumber
lablist.append(labdict)
return JsonResponse(json.dumps(lablist))
As suggested in the comments, using Django Rest Framework or a similar package would be an improvement. As a general rule, in Django or other ORMs, you want to avoid looping over a queryset like this and adjusting each element. Why not serialize the queryset itself and do the logic that's in this loop in your template or other consumer?
You are return the response in for loop so that loop break on 1st entry
import json
some_list = []
for i in data:
some_list.append({"key": "value"})
return HttpResponse(json.dumps({"some_list": some_list}), content_type="application/json")
Try above example to solve your problem
Given the example below, is there a more elegant solution than passing a string to allow a function to test against and in turn, run the required code?
myfunction(self, "location1")
def myfunction(self, source):
if source == "location1":
qs = MyModel.objects.filter(id = self.object.id)
return qs
elif source == "location2":
qs = AnotherModel.objects.filter(id = self.object.id)
return qs
else:
qs = YetAnotherModel.objects.filter(id = self.object.id)
return qs
This example contains dummy Django queries, but I've had to use this solution on various Python functions throughout my projects.
I think this way is cleaner:
def myfunction(self, source):
models = { "location1": MyModel,
"location2": AnotherModel,
"location3": YetAnotherModel
}
selected = models.get(source, YetAnotherModel)
qs = selected.objects.filter(id = self.object.id)
return qs
I think this one may be ok:
from collections import defaultdict
def myfunction(source):
return defaultdict(lambda: YetAnotherModel.objects.filter(id = self.object.id),
{ "location1": MyModel.objects.filter(id = self.object.id),
"location2": AnotherModel.objects.filter(id = self.object.id) })
[source]
Hope this helps!
class Hero(models.Model):
talents = models.ManyToManyField(Talent)
(...)
class Talent(models.Model)
required_talents = models.ManyToManyField('self', symmetrical=False)
(...)
I would like to create method has_required_talents(self, talent) for Hero, which will check if this Hero has required_talents for chosen Talent.
I tried this:
def has_required_talents(self, talent)
required_list = talent.talents_required.values_list('id', flat=True)
hero_talents = self.hero.talents.values_list('id', flat=True)
if required_list in hero_talents:
return True
return False
However, it doesn't work properly when I test it, using these tests:
class HeroTalents(TestCase):
def setUp(self):
self.hero=Hero('Duck')
self.hero.save()
def test_has_required_talents(self):
self.talent1 = Talent(name = "Overpower")
self.talent1.save()
self.talent2 = Talent(name = "Overpower2")
self.talent2.save()
self.talent2.talents_required.add(self.talent1)
self.assertFalse(self.hero.has_required_talents(self.talent2), "test1")
self.hero.talents.add(self.talent1)
self.assertTrue(self.hero.has_required_talents(self.talent2), "test2")
self.talent3 = Talent(name = "Overpower3")
self.talent3.save()
self.hero.talents.add(self.talent2)
self.assertTrue(self.hero.has_required_talents(self.talent3), "test3")
self.talent1.talents_required.add(self.talent3)
self.assertFalse(self.hero.has_required_talents(self.talent1), "test4")
What needs to be done to make this work?
def has_required_talents(self, talent)
required_list = talent.talents_required.values_list('id', flat=True)
hero_talents = self.hero.talents.values_list('id', flat=True)
for item in required_list:
if not item in hero_talents:
return False
return True
That would be a list-based approach. You can also convert them to sets:
def has_required_talents(self, talent)
required_list = set(talent.talents_required.values_list('id', flat=True))
hero_talents = set(self.hero.talents.values_list('id', flat=True))
if required_list.issubset(hero_talents):
return True
return False
But (I don't know about exact internals, you could run some tests) the first approach should be quicker, because there is no conversion.
Apologies if this question has already been asked but I do not think I know the correct terminology to search for an appropriate solution through google.
I would like to select an object from a list of objects by the value of it's attribute, for example:
class Example():
def __init__(self):
self.pList = []
def addPerson(self,name,number):
self.pList.append(Person(self,name,number))
class Person():
def __init__(self,name,number):
self.nom = name
self.num = number
a = Example()
a.addPerson('dave',123)
a.addPerson('mike',345)
a.pList #.... somehow select dave by giving the value 123
in my case the number will always be unique
Thanks for the help
One option is to use the next() built-in:
dave = next(person for person in a.pList if person.num == 123)
This will throw StopIteration if nothing is found. You can use the two-argument form of next() to provide a default value for that case:
dave = next(
(person for person in a.pList if person.num == 123),
None,
)
A slightly more verbose alternative is a for loop:
for person in a.pList:
if person.num == 123:
break
else:
print "Not found."
person = None
dave = person
If those nom's are unique keys, and all you are ever going to do is access your persons using this unique key you should indeed rather use a dictionary.
However if you want to add more attributes over time and if you like to be able to retrieve one or more person by any of those attributes, you might want to go with a more complex solution:
class Example():
def __init__(self):
self.__pList = []
def addPerson(self,name,number):
self.__pList.append(Person(name,number))
def findPerson(self, **kwargs):
return next(self.__iterPerson(**kwargs))
def allPersons(self, **kwargs):
return list(self.__iterPerson(**kwargs))
def __iterPerson(self, **kwargs):
return (person for person in self.__pList if person.match(**kwargs))
class Person():
def __init__(self,name,number):
self.nom = name
self.num = number
def __repr__(self):
return "Person('%s', %d)" % (self.nom, self.num)
def match(self, **kwargs):
return all(getattr(self, key) == val for (key, val) in kwargs.items())
So let's assume we got one Mike and two Dave's
a = Example()
a.addPerson('dave',123)
a.addPerson('mike',345)
a.addPerson('dave',678)
Now you can find persons by number:
>>> a.findPerson(num=345)
Person('mike', 345)
Or by name:
>>> a.allPersons(nom='dave')
[Person('dave', 123), Person('dave', 678)]
Or both:
>>> a.findPerson(nom='dave', num=123)
Person('dave', 123)
The terminology you need is 'map' or 'dictionnary' : this will lead you to the right page in the python doc.
Extremely basic example:
>>> a = {123:'dave', 345:'mike'}
>>> a[123]
'dave'
The missing underscore makes plist a public property. I don't think that's what you want, since it does not encapsulate the functionality and you could call a.plist.append instead of a.addPerson.
class Example():
...
def filter(self, criteria):
for p in self.plist:
if criteria(p):
yield p
def getByNum(self, num):
return self.filter(lambda p: p.num == num)
dave = next(a.getByNum(123))
If the numbers are unique, you may also consider using a dictionary that maps from number to name or person instead of a list. But that's up to your implementation.
I'm finding this a bit tricky! Maybe someone can help me on this one
I have the following model:
class Unicorn(models.Model):
horn_length = models.IntegerField()
skin_color = models.CharField()
average_speed = models.IntegerField()
magical = models.BooleanField()
affinity = models.CharField()
I would like to search for all similar unicorns having at least 3 fields in common.
Is it too tricky? Or is it doable?
You should use Q objects. The rough example is:
from django.db.models import Q
from itertools import combinations
# this -- the unicorn to be matched with
attr = ['horn_length', 'skin_color', 'average_speed', 'magical', 'affinity']
q = None
for c in combinations(attrs, 3):
q_ = Q(**{c[0]: getattr(this, c[0])}) & Q(**{c[1]: getattr(this, c[1])}) & Q(**{c[2]: getattr(this, c[2])})
if q is None:
q = q_
else:
q = q | q_
Unicorn.objects.get(q)
not tested, though
It has to be done in the HAVING clause:
SELECT ... HAVING (IF(a.horn_length=b.horn_length, 1, 0) + ...) >= 3
There's no way to express HAVING in the Django ORM so you'll need to drop to raw SQL in order to perform it.
This should cover your question, if I understood it right:
from django.db import models
Unicorn.objects.filter(models.Q(skin_color = 'white') | models.Q(magical = True))
This would filter all unicorns that have skin color white or have some magical stuff in common. More about the Q objects here http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
I have never used Django and i'm rather novice in Python but perhaps you can do something like this:
make a method that compares two instances of the class Unicorn.
def similarity(self, another)
sim = 0
if (self.horn_length==another.horn_length):
sim+=1
if (self.skin_color==another.skin_color):
sim+=1
if (self.average_speed==another.average_speed):
sim+=1
if (self.magical==another.magical):
sim+=1
if (self.affinity==another.affinity):
sim+=1
return sim
Then you can test with something like:
myUnicorn
for x in unicornsList:
if myUnicorn.similarity(x) >=3:
...