Django annotate - get attribute of all related objects - python

I have such a models:
# models.py
class A(Model):
...
class B(Model):
parent = ForeignKey(A)
comments = ForeignKey(Comments)
class Comments(Model):
title = CharField(...)
How, using annotate method of queryset, I can get all titles of Bs' comments related to some A queryset?
I tried:
result = A.object.all().annotate(b_titles=F("b__comments__title"))
but it returns only first object.
FilteredRelation also didnt help (FilteredRelation's condition doesn't support nested relations)

I solved this problem with ArrayAgg (https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/aggregates/)
- available only using Postgres.

Related

Stop affecting other objects of Django Many to Many model

I'm trying to replicate Blood Group as Model as defined in this picture.
.
In my models.py file I had my code to replicate the blood groups like this
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self")
receives = models.ManyToManyField("self")
def __str__(self):
return self.name
And in my admin.py file I had registered the model as follows
class BloodGroupAdmin(admin.ModelAdmin):
model = BloodGroup
list_display = ['name', 'get_gives', 'get_receives']
def get_gives(self, obj):
return ", ".join([item.name for item in obj.gives.all()])
def get_receives(self, obj):
return ", ".join([item.name for item in obj.receives.all()])
admin.site.register(BloodGroup, BloodGroupAdmin)
Initially I created plain BloodGroup objects without their gives and receives attribute by providing just their names alone. Thus I create an object for all 8 types. Then as I added relationships to each object I found that adding gives or receives for one object affects other objects gives and receives too, making it impossible to replicate the structure in image.
How do I define relationships, without affecting other objects?
In my admin site, I see field names as "get_gives" and "get_receives". How would i make the admin page show field names as "gives" and "receives" but still displaying objects as strings like the image below?
For first question, probably it is better to have only one relation gives. receives can be found from the reverse query. Like this:
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self", related_name="receives", symmetrical=False)
Then you only need to add objects to gives. receives will be generated automatically.
For second question, add short_description attribute to function(reference to docs). Like this:
get_gives.short_description = 'Gives'
get_receives.short_description = 'Receives'

Django - Problem adding a queryset to a field on a FilterSet

I have some filtersSets working fine, but now I tried to add a queryset to a field on the FilterSet and it fails when I load the page.
I'm using Django 2.1.1 with Python 3.6 and Django-filter 2.0.0.
view
def search_jobs(request):
job_list = Job.objects.filter(project__status="P", is_deleted="False")
job_filter = JobsListFilter(request.GET, queryset=job_list)
return render(request, 'webplatform/jobs_list_filter.html', {'filter': job_filter})
filter
class JobsListFilter(django_filters.FilterSet):
# LINE ADDED - the following line is the added queryset
projects = Project.objects.filter(status="P", is_deleted="False")
skills = WorkerSkills.objects.filter(
id__in=Job.objects.values_list('required_skills__id', flat=True).distinct())
worker_job = django_filters.CharFilter(method='my_filter')
required_skills = django_filters.ModelMultipleChoiceFilter(queryset=skills, widget=forms.SelectMultiple)
# LINE ADDED - The following line is the one that adds the queryset inside the field I want to filter.
project = django_filters.ChoiceFilter(queryset=projects)
compensation_type = django_filters.ChoiceFilter(choices=Job.COMPENSATION_TYPE, widget=forms.RadioSelect)
class Meta:
model = Job
fields = ['worker_job', 'required_skills', 'project', 'compensation_type']
def my_filter(self, queryset, worker_job, value):
return queryset.filter(
worker_job__icontains=value
) | queryset.filter(
work_description__icontains=value
)
The code is working without the added lines LINE ADDED on the FilterSet. But the thing is that on the field project it just let me select between all the projects created, and I want to have only the ones that are really necessary (applying the queriset on the code).
But adding those lines in the code, when I use debug mode I can see the queryset applied on the field project giving the expected results. But then, on the return of the view is throwing the following error.
TypeError at /platform/search/jobs/
__init__() got an unexpected keyword argument 'queryset'
So I don't know what I did wrong, because I'm using the same structure used on the required_skills field adding a queryset with only the objects that I want and it should work.
As #Moisés Hiraldo said on the comments, the problem is that I had to use django_filters.ModelChoiceFilter in front of django_filters.ChoiceFilter.

Django queryset filter by post variable

I am trying to do a queryset filter using Django. I have Coupon objects with a code, which is a CharField. I want to find all Coupon objects with a matching code.
My model:
class Coupon(models.Model):
code = models.CharField(max_length=50)
... other fields
My view:
# This method returns the queryset
Coupon.objects.filter(code = "abc123")
# This part of the code is not working the way I want to
couponCode = str(request.POST.get("code"))
Coupon.objects.filter(code = couponCode)
I have ensured that the POST variable is "abc123" but I am still getting an empty queryset in the second query.
Just use
couponCode = request.POST['code']
instead of
couponCode = str(request.POST.get("code"))
Remove the str() part. Leave it only as:
couponCode = request.POST.get("code"),
then you could do:
Coupon.objects.filter(code=couponCode)
Hope this helps.

django-rest-framework 3.0 create or update in nested serializer

With django-rest-framework 3.0 and having these simple models:
class Book(models.Model):
title = models.CharField(max_length=50)
class Page(models.Model):
book = models.ForeignKey(Books, related_name='related_book')
text = models.CharField(max_length=500)
And given this JSON request:
{
"book_id":1,
"pages":[
{
"page_id":2,
"text":"loremipsum"
},
{
"page_id":4,
"text":"loremipsum"
}
]
}
How can I write a nested serializer to process this JSON and for each page for the given book either create a new page or update if it exists.
class RequestSerializer(serializers.Serializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
I know that instantiating the serializer with an instance will update the current one but how should I use it inside the create method of nested serializer?
Firstly, do you want to support creating new book instances, or only updating existing ones?
If you only ever wanted to create new book instances you could do something like this...
class PageSerializer(serializers.Serializer):
text = serializers.CharField(max_length=500)
class BookSerializer(serializers.Serializer):
page = PageSerializer(many=True)
title = serializers.CharField(max_length=50)
def create(self, validated_data):
# Create the book instance
book = Book.objects.create(title=validated_data['title'])
# Create or update each page instance
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=book)
page.save()
return book
Note that I haven't included the book_id here. When we're creating book instances we won't be including a book id. When we're updating book instances we'll typically include the book id as part of the URL, rather than in the request data.
If you want to support both create and update of book instances then you need to think about how you want to handle pages that are not included in the request, but are currently associated with the book instance.
You might choose to silently ignore those pages and leave them as they are, you might want to raise a validation error, or you might want to delete them.
Let's assume that you want to delete any pages not included in the request.
def create(self, validated_data):
# As before.
...
def update(self, instance, validated_data):
# Update the book instance
instance.title = validated_data['title']
instance.save()
# Delete any pages not included in the request
page_ids = [item['page_id'] for item in validated_data['pages']]
for page in instance.books:
if page.id not in page_ids:
page.delete()
# Create or update page instances that are in the request
for item in validated_data['pages']:
page = Page(id=item['page_id'], text=item['text'], book=instance)
page.save()
return instance
It's also possible that you might want to only support book updates, and not support creation, in which case, only include the update() method.
There are also various ways you could reduce the number of queries eg. using bulk create/deletion, but the above would do the job in a fairly straightforward way.
As you can see there are subtleties in the types of behavior you might want when dealing with nested data, so think carefully about exactly what behavior you're expecting in various cases.
Also note that I've been using Serializer in the above example rather than ModelSerializer. In this case it's simpler just to include all the fields in the serializer class explicitly, rather than relying on the automatic set of fields that ModelSerializer generates by default.
You can simply use drf-writable-nested.
It automatically make your nested serializers writable and updatable.
in you serializers.py:
from drf_writable_nested import WritableNestedModelSerializer
class RequestSerializer(WritableNestedModelSerializer):
book_id = serializers.IntegerField()
page = PageSerializer(many=True)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
And that's it!
Also the library supports using only one of the create and update logics if you don't need both.

Having some ModelForms at the same page with some similar Field Name

I've put forms of these model(via ModelForm) at the same page but similar Field Names have made some problem for me:
class Transfer(models.Model):
ServiceProvider=models.ForeignKey(ServiceProvider)
Vehicle=models.ForeignKey(Vehicle)
From=models.ForeignKey(Location,related_name='FromLocation')
To=models.ForeignKey(Location,related_name='ToLocation')
PriceUnit=models.ForeignKey(PriceUnit)
Class=models.ForeignKey(Class)
Price=models.CharField(max_length=12)
Date=models.CharField(max_length=16)
class Visit(models.Model):
Location=models.ForeignKey(Location)
FromDate=models.CharField(max_length=16)
ToDate=models.CharField(max_length=16)
class Accommodate(models.Model):
Location=models.ForeignKey(Location)
PriceUnit=models.ForeignKey(PriceUnit)
FromDate=models.CharField(max_length=16)
ToDate=models.CharField(max_length=16)
Price=models.CharField(max_length=12)
I googled and just found about formset,How can I solve this problem?
You can pass the forms a prefix e.g.
transfer = TransferForm(prefix="transfer")
visit = VisitForm(prefix="visit")
accommodate = AccommodateForm(prefix="accommodate")

Categories

Resources