Django REST Framework, display specific foreign key data objects - django

I'll get straight to my point
Here is my API output:
In pictures, I need to display objects that have primary_placeholder set to True.
I can't find answer on how to do it, I've tried searching in REST Api documentation but nothing seems to work, it always displays every single picture
serializers.py:
views.py
Models.py (Theese two that I'm currently working with)
Does anyone know what is the easiest and fastest way to do it? I have absolutely no idea and I've been trying figure it out for about 2 days now.
Database currently has no pictures with primary_placeholder set to True if anyone is confused

If want to show product pictures with primary_placeholder=True, we can use prefetch_related with Prefetch, please read this.
In your case above, i suggest:
views.py
from django.db import models
from your_app.models import Product, ProductPictures
class ProductList(APIView):
def get(self, request, format=None):
products = Product.objects.all().prefetch_related(
models.Prefetch(
"pictures",
queryset=ProductPictures.objects.filter(primary_placeholder=True)
)
)
serializer = ProductSerializer(products, many=True)
return Response(serializer.data)
Another suggest: In model ProductPictures field model, better if change the field name to product.
Good luck.

write a serializer method field and filter products in it.
class ProductSerializer(serializers.ModelSerializer):
filtered_pictures = serializers.MethodField()
class Meta:
model = Product
fields = ['brand','model','prize','filtered_pictures']
def get_filtered_pictures(self,obj):
if obj.pictures.exists():
queryset = obj.pictures.filter(primary_placeholder=True)
return ProductPictureSerializer(queryset, many=True).data
else:
return None

Related

Django: When to put code in serializers.py or views.py

I am kind of confused about when should I put code in serializers.py or views.py.
My understanding is that serializers.py is where data is manipulated + converted between front and backend.
But so far, my understanding is that only the validation function and maybe a Meta class need to be in serializers.py. I feel like I can just manipulate the database directly in views.py. Like, I can just import the model in views.py and then do datModel.objects.create() or datModel.objects.get().someAttribute = somethingNew.
Since you mentioned Models and Views, my thinking is that the less logic about manipulating the model in the view the better, leave the view to focus on fetching the data, minimum data manipulation code via encapsulation, then present the data to the view
here is the overly simplified example
class Product(models.Model):
...
stock_level = model.Integer(..)
def reduce_stock_level(number)
# insert some 100 lines of validation
self.stock_level -= number
self.save()
In your view:
def checkout(...):
...
product = Product.object.get(...)
product.reduce_stock_level(1)
is cleaner than in the view to do the same thing
def checkout(...):
product = Product.object.get(...)
# insert 100 lines of validation
product.stock_level -= 1
product.save()
Also you can reuse the method in other places and seems more pythonic:
for product in Products.objects.all():
product.reduce_stock_level(10)

Django Admin: list_display() order by count of related object

Imagine you have a model Person which can have N Book instances.
I want to display the number of books a person has in the django admin change-list view.
It is easy to display the number (write a custom method on PersonAdmin).
But I could not find a way to make it sortable by the number of books a person has.
I read the docs for list_display, but could not find an answer.
According to the docs a query expression could be used. Is it possible to solve it this way?
After applying the answer, this is what it looks like. In this case each instance has N log entries:
Override the get_queryset() method of model admin with the help of annoate() and order_by() methods. Then, define a callable function(book_count()) to return the value to the change-list page
from django.db.models.aggregates import Count
class PersonAdmin(admin.ModelAdmin):
list_display = ('book_count', 'other_field')
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.annotate(book_count=Count('book')).order_by('-book_count')
return qs
def book_count(self, person_instance):
return person_instance.book_count
Why I didn't add ordering ?
While we calling the super(), Django will throw exceptions

Django ModelForm hide field from form and use value from url

Thanks in advance for reading this. I can't wrap my head around it and it's getting quite frustrating by now.
We have the following registration form:
class RegistrationForm(forms.ModelForm):
class Meta:
model = Register
fields = ('name', 'company_name')
def clean(self):
if is not self.cleaned_data.get('card').is_available():
raise forms.ValidationError(_('Error'))
The Register model includes a card linked to a Card model. This includes is_available() which functionally works.
Our flow is:
The end user selects the card which lists all registrations for it.
They click the 'Add registration'-button which brings them to cards/{PK}/add.
The Add registration-button is a generic.View. In post(self, request, pk) I have the following code:
form = RegistrationForm(request.POST)
But how do I pass it the contents of Card.objects.get(pk=pk) to it?
I tried:
data = request.POST.copy()
data['card'] = pk
form = RegistrationForm(data)
But I think because card is not included in fields it gets lost somewhere, which makes sense from a sanitize-all-input-point of view, but I would very much like to add the card dynamically, in this case.
Any ideas?
So, just use CreateView and study how it does things using the linked site.
There is no need to use generic.View as it's the basic of basics. You only want to implement all this logic using generic.View to get more familiar with the way things work or if you need some very special form handling.
The short version would be:
from django.views import generic
from myapp.forms import RegistrationForm
class CardCreateView(generic.CreateView):
form_class = RegistrationForm
ModelForm has a save method. The correct way to solve this is to use it with commit=False, that will return an object that hasn’t yet been saved to the database. Then you can alter that object before finally saving it.
This is explained here in the docs
So this is what your code should look like:
form = RegistrationForm(request.POST)
form.save(commit=False)
form.card = Card.objects.get(pk=pk)
form.save_m2m()
save_m2m should be used if your model has many-to-many relationships with other models. In my case, it was a OneToOne, so I used save() instead.
If you use a CreateView instead of the generic View, the snippet above should go into your overridden form_valid method

Pre-selecting multiple checkboxes in Django forms

Very similar to this question, but I tried the accepted answer and it did not work. Here's what's going on.
I have a form for tagging people in photos that looks like this:
forms.py
class TaggingForm(forms.Form):
def __init__(self, *args, **kwargs):
queryset = kwargs.pop('queryset')
super(TaggingForm, self).__init__(*args, **kwargs)
self.fields['people'] = forms.ModelMultipleChoiceField(required=False, queryset=queryset, widget=forms.CheckboxSelectMultiple)
...
models.py
class Photo(models.Model):
user = models.ForeignKey(User)
...
class Person(models.Model):
user = models.ForeignKey(User)
photos = models.ManyToManyField(Photo)
...
I want users to be able to edit the tags on their photos after they initially tag them, so I have a page where they can go to view a single photo and edit its tags. For obvious reasons I want to have the already-tagged individuals' checkboxes pre-selected. I tried to do this by giving the form's initial dictionary a list of people I wanted selected, as in the answer to the question I linked above.
views.py
def photo_detail(request,photo_id):
photo = Photo.objects.get(id=photo_id)
initial = {'photo_id':photo.id, 'people':[p for p in photo.person_set.all()]}
form_queryset = Person.objects.filter(user=request.user)
if request.method == "POST":
form = TaggingForm(request.POST, queryset=form_queryset)
# do stuff
else:
form = TaggingForm(initial=initial, queryset=form_queryset)
...
When I try to initialize people as in the above code, the form doesn't show up, but no errors are thrown either. If I take the 'people' key/value pair out of the initial dictionary the form shows up fine, but without any people checked.
Also I'm using Django 1.5 if that matters. Thanks in advance.
What you could do is simply use django forms to handle all of this for you. Please refer to this question. Ideally it boils down to lettings djnago handle your forms and its validation and initial values.
Now this is actually a really good practice to get used to since, you're dissecting all your logic and your presentation. Its a great DRY principle.

Can I create a custom django modelchoicefield with a default queryset

I have an order model with a followed_by field:
class order(models.Model):
followed_by = models.ForeignKey(User, limit_choices_to={'groups__name': "Managers"})
I have several such models and forms for those models. By default the form displays a modelchoicefield listing users that are mangers. This is fine. But the display isn't nice: it gives the username, and I want first+last name. This would work nicely: Change Django ModelChoiceField to show users' full names rather than usernames
except that now in everyform I must declare the queryset to limit users to managers. Can I use the above method so that the custom modelchoicefield defaults to my filtered queryset. so then from a form I can just say:
followed_by = ManagerUserModelChoiceField()
Can you define the queryset on your ModelChoiceField child class?
class UserModelChoiceField(ModelChoiceField):
# Query that returns set of valid choices
queryset = User.objects.filter(group__name='Managers')
def label_from_instance(self, obj):
return obj.get_full_name()
Try passing in the queryset as an argument to the ManagerUserModelChoiceField class.
followed_by = ModelChoiceField(queryset = User.objects.filter(groups__name="Managers")
After my comment to #Enrico this thought occurred to me: I overwrote the "init" class on my custom field like so:
class UserModelChoiceField(forms.ModelChoiceField):
def __init__(self, *args, **kwargs):
super(UserModelChoiceField, self).__init__(queryset=User.objects.filter(groups__name="Managers"), *args, **kwargs)
I've seen stuff like this done in python before but I'm new to python so I'm not sure if this is a bad thing to do or if I should make this better somehow? I'd appreciate some feedback. That being said, it seems to be working correctly.