Django filter ManyToMany filed in query - django

I need to filter field with ManyToMany in queryset. There must stay only active Products
class Product(model.Model):
name = models.CharField()
active = models.BooleanField(blank=True, default=True)
class Package(model.Model):
name = models.CharField()
products = models.ManyToManyField(Product)
I tried something like this.
packages = Package.objects.all()
for package in packages:
active_products = package.products.filter(active=True)
package.products = active_products
but it updates my packages in database, when i need only change queryset.
expectation concept (do not really need nested structure, query set is fine):
packages = [
{'id': 1, 'name': 'First package', 'products': [
{'id': 1, 'name': 'first product', 'active': True},
{'id': 2, 'name': 'second product', 'active': True},
]},
{'id': 2, 'name': 'Second package', 'products': [
{'id': 2, 'name': 'first product', 'active': True},
{'id': 3, 'name': 'third product', 'active': True},
]},
{'id': 3, 'name': 'Third package', 'products': []}
]
I thought about creating a list of dictionaries from Packages by .values(), then iterate it and exclude all not active products.
Do you know any more elegant way to do it?

I'm not sure if this will work. But I think that we can play using the serializers.MethodSerialier. If it fails probably we can try to return another thing instead of the Productserializer directly.
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id', 'name')
class PackageSerializer(serializers.ModelSerializer):
products = serializers.MethodSerializer()
class Meta:
model = Package
fields = ('id', 'name', 'products')
def get_products(self, obj):
products = obj.products.filter(active=True)
return ProductSerializer(products, many=True)
packages = Package.objects.filter(products__active=True)
serializer = PackageSerializer(packages, many=True)
# do something with the serializer

AFAIK, you won't get a nested output from Django :( But you will get something similar with .values() method of QuerySet class as,
results = Package.objects.filter(products__active=True).values('id', 'name', 'products__id', 'products__name', 'products__active')
filtered_result = [result for result in results if result['products__active']]

Related

showing date from db in html date in django

How can i show date from db in django html template with input type date.
As You can see if we use input type 'date'. we can see the below widget showing in html. But if i want to it to show date from DB in below picture. Is there anyway i can do?
Showing like this, so user can see the current date from DB and change if needed.
forms.py
class ProfileUpdateForm(ModelForm):
#birthdate = forms.DateField(
# input_formats=['%dd-%mm-%yyyy'],
# widget=forms.DateInput(format='%dd-%mm-%yyyy', attrs={'type': "date"})
# )
class Meta:
model = User
fields = [
'email',
'full_name',
'address',
'city',
'state',
'postcode',
'phone_number',
'birthdate',
]
widgets = {
'full_name': Textarea(attrs={'cols': 35, 'rows': 1}),
'address': Textarea(attrs={'cols': 80, 'rows': 3}),
'city': Textarea(attrs={'cols': 35, 'rows': 1}),
'birthdate': forms.DateInput(format='%d %b %Y', attrs={'type':'date'}),
}

Data Dissapearing in Django on validation

I am trying to create nested objects (documentregulation) when I create a Document object. To achieve this, I have overwritten the create method on the DocumentSerializer, as per Django Docs. However, when I attempt validated_data.pop('documentregulation_set'), it is empty, even when it is populated in the incoming request.data of my view. Is there something causing my incoming data to not be validated? How would I go about debugging this if so?
// serializers.py
class DocumentRegulationSerializer(serializers.ModelSerializer):
class Meta:
model = DocumentRegulation
fields = ('regulation',)
class DocumentSerializer(serializers.ModelSerializer):
documentregulation_set = DocumentRegulationSerializer(many=True, required=False)
class Meta:
model = Document
fields = ('documentregulation_set', 'id', 'name', 'file', 'text', 'uploaded_at')
extra_kwargs = {
'id': {'read_only': True},
'uploaded_at': {'read_only': True},
}
def create(self, validated_data):
documentregulation_set = validated_data.pop('documentregulation_set')
# create document first
document = Document.objects.create(**validated_data)
# serialize associated regulations
for documentregulation in documentregulation_set:
# get ID of newly-created document, use for relation
#documentregulation['regulation'] = documentregulation['id']
DocumentRegulation.objects.create(document=document, **documentregulation)
return document
//views.py
class DocumentView(generics.ListCreateAPIView):
def create(self, request):
#pprint(self.request.FILES['profile_pic'])
request.data['documentregulation_set'] = json.loads(request.data['documentregulation_set'])
request.data['documentdomain_set'] = json.loads(request.data['documentdomain_set'])
pprint(request.data)
document = DocumentSerializer(data=request.data)
if document.is_valid():
document.save()
return Response(document.data, status=status.HTTP_201_CREATED)
else:
return Response(document.errors, status=status.HTTP_400_BAD_REQUEST)
my incoming data (printed in request.data) looks like:
{'documentregulation_set': [{'label': 'Regulation 1',
'regulation': 2,
'value': 2},
{'label': 'Regulation 2',
'regulation': 4,
'value': 4}],
'file': <InMemoryUploadedFile: test.docx >,
'name': 'testing',
'text': 'test'}
but then my validated data prints out to be:
{'documentregulation_set': [],
'file': <InMemoryUploadedFile: test.docx >,
'name': 'testing',
'text': 'test'}
Problem seems to be with the validation of the documentregulation_set field in the DocumentSerializer serializer. But you can escape the validation in the Meta Class as:
class Meta:
model = Document
fields = '__all__'
extra_kwargs = {
'documentregulation_set': {'validators': []} # escape validation
}
If you need to write custom validators have a look at Writing custom validators
So the final serializer looks like:
class DocumentRegulationSerializere(serializers.ModelSerializer):
"""Serializers for DocumentRegulation object"""
class Meta:
model = DocumentRegulation
fields = ('regulation',)
class DocumentSerializer(serializers.ModelSerializer):
"""Serializer for Document objects"""
documentregulation_set = DocumentRegulationSerializere(many=True)
class Meta:
model = Document
fields = ('documentregulation_set', 'id', 'name', 'file', 'text', 'uploaded_at')
extra_kwargs = {
'id': {'read_only': True},
'uploaded_at': {'read_only': True},
'documentregulation_set': {'validators': []} # escape validation
}
def create(self, validated_data):
doc_reg = []
document_regulation_set = validated_data.pop('documentregulation_set')
document = Document.objects.create(**validated_data)
for document_regulation in document_regulation_set:
reg = DocumentRegulation.objects.create(**document_regulation)
reg.save()
doc_reg.append(reg)
document.documentregulation_set.add(reg)
return document
View
class DocumentView(generics.ListCreateAPIView):
"""List or create new Document ojects"""
queryset = Document.objects.all()
serializer_class = DocumentSerializer

django rest framework- how can I pass query params to foreign key serializer?

For my application, I have Locations, and Events given by the following (simplified) models:
class Location(models.Model):
name = models.CharField(max_length=64)
class Event(models.Model):
location = models.ForeignKey(Location)
date = models.DateField()
In my API, I have an endpoint: /api/locations/ which returns all locations, and each location has its events embedded within it, e.g.:
[
{
'id': 1,
'name': 'Location 1',
'events': [
{'id': 1, 'date': '2018-01-01'},
{'id': 2, 'date': '2018-01-14'}
]
},
{
'id': 2,
'name': 'Location 2',
'events': [
{'id': 3, 'date': '2017-12-28'},
{'id': 4, 'date': '2018-01-10'}
]
}
]
my serializers look like:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = ('id', 'date',)
read_only_fields = ('id',)
class LocationSerializer(serializers.ModelSerializer):
events = EventSerializer(many=True)
class Meta:
model = Location
fields = ('id', 'name', 'events',)
read_only_fields = ('id',)
Using ViewSets, I would like to add a filter to this call, with a max_date, and min_date parameter, e.g.: /api/locations/?min_date=2018-01-01&max_date=2018-01-15. Which should return only locations that have events between those two dates, AND should only return the events for each location between those two dates.
In the example data above, both locations would return, Location 1 would have both events listed, but Location 2 would only have the second event in its list. In my ViewSet, I can take those parameters and append them as queryset filters:
class LocationViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
serializer_class = LocationSerializer
def get_queryset(self):
queryset = Location.objects.all()
min_date = self.request.query_params.get('min_date', None)
max_date = self.request.query_params.get('max_date', None)
if min_date is not None and max_date is not None:
queryset = queryset.filter(event__date__lte=max_date, event__date__gte=min_date)
return queryset
Which will filter out any locations that do not have any events between the two dates, however those returned locations show ALL of their events, not just the ones between min_date and max_date. I assume I have to somehow pass my query parameters on to the EventSerializer embedded within LocationSerializer, but am unsure as to how I might do this.
Try to prefetch filtered events in view:
from django.db.models import Prefetch
class LocationViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
serializer_class = LocationSerializer
def get_queryset(self):
queryset = Location.objects.all()
min_date = self.request.query_params.get('min_date', None)
max_date = self.request.query_params.get('max_date', None)
if min_date is not None and max_date is not None:
queryset = queryset.filter(event__date__lte=max_date, event__date__gte=min_date).prefetch_related(Prefetch('events', queryset=Event.objects.filter(date__lte=max_date, date__gte=min_date))
return queryset
Also you can use serializerMethodField to filter events:
class LocationSerializer(serializers.ModelSerializer):
events = SerializerMethodField()
class Meta:
model = Location
fields = ('id', 'name', 'events',)
read_only_fields = ('id',)
get_events(self, obj):
min_date = self.context['request'].query_params.get('min_date', None)
max_date = self.context['request'].query_params.get('max_date', None)
events = obj.events.filter(date__lte=max_date, date__gte=min_date)
return LocationSerializer(events, many=True).data
With self.context['request'] you can get request data inside serializer.

django rest framework: limit fields that can be updated

I want users to be able to update only one specific field. For example:
models.py
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ('created',)
serializer.py
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
views.py
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
Once the Snippet is created, the user should only be able to update title field.
I know I can achieve that by something like this:
serializers.py
def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the validated data.
"""
instance.title = validated_data.get('title', instance.title)
instance.save()
return instance
In serializer class. But I want to know, is there a way that browsable API show only title field in edit form? And also skip validation for fields that are not required?
Django REST Framework provides the read_only and write_only attributes for controlling what is used for editing and what is not.
serializers.py
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
extra_kwargs = {
'id': {'read_only': True},
'code': {'read_only': True},
'lineos': {'read_only': True},
'language': {'read_only': True},
'style': {'read_only': True}
}
The above will return all the fields on read requests but only title will be writable.
You can find more at the official documentation:
http://www.django-rest-framework.org/api-guide/serializers/#specifying-read-only-fields
While #petkostas answer is correct, it doesn't give you a full picture of how to achieve it.
First, Create a new serializer; let's call it SnippetUpdateSerializer
Now, you may have custom serializer fields like serializers.MethodFieldSerializer that you would have defined in SnipperSerializer; which you may not want to write again in your new serializer. A good approach is to use inheritance.
Taking the example from the question
class SnippetUpdateSerializer(SnippetSerializer): #<- pay attention here
class Meta(SnippetSerializer.Meta): # <- pay attention here
SnippetSerializer.Meta.extra_kwargs.update({ # update the dictionary
'id': {'read_only': True},
'code': {'read_only': True}, # you can also use {write_only: True} if you want this to be write only
'lineos': {'read_only': True},
'language': {'read_only': True},
'style': {'read_only': True}
}) # you may completely override by just using extra_kwargs, instead of SnippetSerializer.Meta.extra_kwargs
Now in your SnippetUpdateView, use the above serializer.
If you are using class based views then set serializer_class = SnippetUpdateSerializer
Another approach is to return bad request, from your update view if the user requests contain read_only fields. (not recommended)

Access Django model instance in ModelForm's FormField definions

in one of my admin forms, I override a model form field (location) in order to use a different, map specific form field (olwidget). This MapField should include a layer (InfoLayerField) that displays all other model instances but the one that is being edited at the moment. Right now, it displays all the model instances (see MyModel.objects.all()) which means, if a model is edited the current location is displayed twice.
In order to achieve this, I'd have to exclude the current edited model instance from the QuerySet used in InfoLayerField (something like MyModel.objects.exclude(pk=self.instance.pk)). But since form fields are defined as static variables, I can't access self.instance.
Is there any way to achieve this?
# models.py
class MyModel(models.Model):
name = models.CharField(max_length=200)
location = models.PointField(blank=True, null=True)
# admin.py
from olwidget.fields import MapField, EditableLayerField, InfoLayerField
from olwidget.utils import get_ewkt
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = MyModel
location = MapField([
EditableLayerField({
'geometry': 'point',
'name': 'location',
}),
InfoLayerField(
[(get_ewkt(m.location), m.name) for m in MyModel.objects.all() if m.location ], {
'geometry': 'point',
'name': 'other locations',
'cluster': True,
'cluster_display': 'list',
}
)
])
class MyModelOlwidgetAdmin(admin.ModelAdmin, GeoModelAdmin):
form = MyModelAdminForm
...
Thanks for any hint's.
I think you just need to override the __init__ on your form:
class MyModelAdminForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self,*args,**kwargs):
super(MyModelAdminForm, self).__init__(*args,**kwargs)
qs = MyModel.objects.exclude(pk = self.instance.pk) #grab instance.pk here
self.fields['location'] = MapField([
EditableLayerField({
'geometry': 'point',
'name': 'location',
}),
InfoLayerField(
[(get_ewkt(m.location), m.name) for m in qs if m.location ], {
'geometry': 'point',
'name': 'other locations',
'cluster': True,
'cluster_display': 'list',
}
)
])