I have models as follows:
class Place(models.Model):
name = models.CharField(max_length=300)
class Person(models.Model):
name = models.CharField(max_length=300)
class Manor(models.Model):
place = models.ManyToManyField(Place, related_name="place"))
lord = models.ManyToManyField(Person, related_name="lord")
overlord = models.ManyToManyField(Person, related_name="overlord")
I want to get all the Places attached with the relation 'lord' to a particular person, and then get the centre, using a GeoDjango method. This is as far as I've got:
person = get_object_or_404(Person, namesidx=namesidx)
manors = Manor.objects.filter(lord=person)
places = []
for manor in manors:
place_queryset = manor.place.all()
for place in place_queryset:
places.append(place)
if places.collect():
centre = places.collect().centroid
However, this gives me:
AttributeError at /name/208460/gamal-of-shottle/
'list' object has no attribute 'collect'
Can I either (a) do this in a more elegant way to get a QuerySet of places back directly, or (b) construct a QuerySet rather than a list in my view?
Thanks for your help!
The way you're doing this, places is a standard list, not a QuerySet, and collect is a method that only exists on GeoDjango QuerySets.
You should be able to do the whole query in one go by following the relations with the double-underscore syntax:
places = Place.objects.filter(manor__lord=person)
Note that your use of related_name="place" on the Manor.place field is very confusing - this is what sets the reverse attribute from Place back to Manor, so it should be called manors.
Related
I want to do a lookup that spans three model classes. I want to find all the PartListings that match the Specific_part in the ListItem. Say Specific part = Radio, there could be several Radios in the PartListings and I want to return the PartListing id of all of them so that I can get other attributes like quantity.
I have these models:
class SpecificPart(BaseModel):
class PartListing(BaseModel):
specific_part = models.ForeignKey(
SpecificPart, on_delete=models.CASCADE, blank=True, null=True,
related_name="part_listing")
class ListItem(BaseModel):
specific_part = models.ForeignKey(SpecificPart, on_delete=models.CASCADE,
related_name="listitem")
I'm trying to put the lookup under the ListItem class like this:
def item_match(self):
part = self.specific_part
return PartListings.filter(specific_part__specific_part=part)
I tried to set it up as a Lookup that spans relationshipsbut am getting an error that PartListing is not defined. I also suspect that I'm referencing the foreign keys incorrectly. I'm also ok with redefining the models if a One to One would be better.
I am a Django newbie so thanks so much for your help!
You can try like this using reverse relation between SpecificPart and PartListing models:
class ListItem(BaseModel):
...
def item_match(self):
return self.specific_part.part_listing.all()
In my project, i have 2 models:
class Product(models.Model):
name = models.CharField(max_length=200)
class Material(models.Model):
name = models.CharField(max_length=200)
product = models.ForeignKey(Product)
Now, I want to make a copy of Product and keep all of the assigned materials.
This is what I tried:
new_product = Product.object.get(pk='someexistingpk')
new_product.pk = None
new_product.name += ' (Copy)'
new_product.save()
Another variant I tried:
new_product = deepcopy(Product.object.get(pk='someexistingpk'))
new_product.pk = None
new_product.name += ' (Copy)'
new_product.save()
But in both cases, the resulting model_set is empty. None of the attached items are kept.
new_product.material_set.all()
<QuerySet []>
How can I resolve this? Ideally without iterating over every item in the original material_set.
Given that ForeignKeys, can not have multiple assignments, it only makes sense that they are not copied over, as that would break the original object.
So, when copying over the Product, one should also do the same for the related objects. Like this:
new_prod = deepcopy(prod)
new_prod.pk = None
new_prod.save()
for mat in prod.material_set.all():
new_mat = deepcopy(mat)
new_mat.pk = None
new_mat.product = new_prod
new_mat.save()
Now, it results in a nice Queryset with all of the material objects attached, as expected.
new_prod.material_set.all()
<QuerySet [<Material: 1.01 Katoen cats and dogs>, <Material: 1.0 Hour-cost>, <Material: 2.0 lint>, <Material: 1.0 Katoen cats and dogs>]>
From my understanding you are trying to duplicate a model. The way I approached this problem in my project was using dynamic models and inheritance. If you use inheritance, all the fields are going to be automatically copied to the new model.
from MyApp.models import Product
class Meta:
pass
setattr(Meta, 'app_label', 'MyApp')
attrs = {'__module__': module, 'Meta': Meta}
model = type('ProductCopy', (Product,), attrs)
So here the new model that is created is called ProductCopy and will have a table on your Django Admin page.
To learn more about dynamic models you can take a look at this documentation.
I have a rather complicated model setup:
class Tournament(models.Model):
pass
class Category(models.Model):
pass
class Discipline(models.Model):
pass
class Judge(models.Model):
pass
class Evaluation(models.Model):
tournament = models.ForeignKey(Tournament)
judge = models.ForeignKey(Judge)
discipline = models.ForeignKey(Discipline)
category = models.ForeignKey(Category)
Of course, those models are incomplete, but their contents aren't important for the problem... Given a Tournament, I need to get the distinct "Category" objects, along with the "Disciplines" evaluated for each of these.
I have tried this:
eval_set = tournament_object.evaluation_set.distinct('category')
categories = [jdgt.categoria for jdgt in eval_set]
Isn't there a cleaner way? I feel like there should be a sort of "extract('category')" that I could call on the queryset...
Any ideas will be appreciated!!!!
Try this:
categories = tournament_object.evaluation_set.distinct( 'category' ).values_list('categoria', flat=True)
Read more on values_list here
I want the user to be able to order a list of objects in a table using javascript. Then, in a django function I would like to sort those object based on the same ordering, not on an attribute.
Is it possible? I was thinking about passing a list of pk from the template to the view and then ordering the objects according to this list, but I have not found a way to do it yet.
I don't think this is possible with queryset. Try following:
pk_list = [2, 1, 3, 4]
pk2obj = {obj.pk: obj for obj in Model.objects.all()}
objects_ordered = [pk2obj[pk] for pk in pk_list]
pkg2obj is mapping between pk and model instance object. To make a dictionary I used dictionary comprehension.
If you want to omit deleted objects:
objects_ordered = [pk2obj[pk] for pk in pk_list if pk in pk2obj]
Else if you want to replace deleted objects with default value (None in following code):
objects_ordered = [pk2obj.get(pk, None) for pk in pk_list]
I've had to solve this exact problem before.
If you want the user to be able to reorder them into a user-defined order, you can easily define a field to store this order.
As you say, initially, you could serve them in order according to id or an upload_date DateTimeField. But you could also have an PositiveIntegerField in the model, named position or order, to represent the user-defined order.
class MediaItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add = True)
position = models.PositiveIntegerField()
Whenever a user changes the order on the frontend, the JS can send the new order as an array of objects (ie. new_order = [{"pk":3, "position":1}, {"pk":1, "position":2}, {"pk":2, "position":3}]). The view can look up each instance by pk, and change the position:
for obj in new_order:
media_item = MediaItem.objects.get(pk=obj['pk'])
media_item.position = obj['position']
media_item.save()
Then always query using
objects_ordered.objects.order_by('position')
That's how we managed to do it. If you have more specific questions regarding this approach, feel free to ask in the comments.
Edit:
If the same object can be a member of many different groups or lists, and you want to store the position of the membership within that list, you can achieve this using a through model. A through model is useful when you need to store data that relates to the relationship between two objects that are related. In addition to the MediaItem class shown above, this is what your other models would look like:
class Album(models.Model):
media_items = models.ManyToManyField(MediaItem,
related_name = 'album_media_items',
through = 'Membership')
class Membership(models.Model):
album = models.ForeignKey(Album,
related_name = 'album')
media_item = models.ForeignKey(MediaItem,
related_name = 'media_item')
date = models.DateTimeField(auto_now_add = True)
position = models.PositiveIntegerField()
Then, you could query the Membership instances, instead of the MediaItem instances.
# get id of list, or album...
alb = Album.objects.get(pk=id_of_album)
media_items = Membership.objects.filter(album=alb).order_by('position')
for item in media_items:
# return the items, or do whatever...
# keep in mind they are now in the user-defined order
You can do this:
pk_list = [1,5,3,9]
foo = Foo.objects.filter(id__in=pk_list)
#Order your QuerySet in base of your pk_list using Lambda
order_foo = sorted(foo, key = lambda:x , pk_list.index(x.pk))
I have a simple hierarchic model whit a Person and RunningScore as child.
this model store data about running score of many user, simplified something like:
class Person(models.Model):
firstName = models.CharField(max_length=200)
lastName = models.CharField(max_length=200)
class RunningScore(models.Model):
person = models.ForeignKey('Person', related_name="scores")
time = models.DecimalField(max_digits=6, decimal_places=2)
If I get a single Person it cames with all RunningScores associated to it, and this is standard behavior. My question is really simple: if I'd like to get a Person with only a RunningScore child (suppose the better result, aka min(time) ) how can I do?
I read the official Django documentation but have not found a
solution.
I am not 100% sure if I get what you mean, but maybe this will help:
from django.db.models import Min
Person.objects.annotate(min_running_time=Min('time'))
The queryset will fetch Person objects with min_running_time additional attribute.
You can also add a filter:
Person.objects.annotate(min_running_time=Min('time')).filter(firstName__startswith='foo')
Accessing the first object's min_running_time attribute:
first_person = Person.objects.annotate(min_running_score=Min('time'))[0]
print first_person.min_running_time
EDIT:
You can define a method or a property such as the following one to get the related object:
class Person(models.Model):
...
#property
def best_runner(self):
try:
return self.runningscore_set.order_by('time')[0]
except IndexError:
return None
If you want one RunningScore for only one Person you could use odering and limit your queryset to 1 object.
Something like this:
Person.runningscore_set.order_by('-time')[0]
Here is the doc on limiting querysets:
https://docs.djangoproject.com/en/1.3/topics/db/queries/#limiting-querysets