Within a listview, with many objects, I want to change their value live by javascript, then save them by a POST/PUT http request to the object updateview, searching I've found that it maybe possible with Django REST framework.
I've read the Django REST framework
manual reference
but didn't understand how to set up the UpdateView call:
model.py
class presetrows(models.Model):
progressivo = models.ForeignKey(preset)
value = models.BigIntegerField(blank=True, null=True)
views.py
class RighePresetListView(ListView):
queryset = presetrows.objects.filter(stato=True)
class RighePresetUpdateView(UpdateView):
model = presetrows
exclude=()
but where should I add the update(request, *args, **kwargs) from django REST?
You don't really needs to define update(request, *args, **kwargs) in DRF views. For update api you can use this
class RighePresetUpdateView(UpdateAPIView):
serializer_class = 'your serializer'
queryset = presetrows.objects.filter(stato=True)
Provides put and patch method handlers implicitly.
Related
Here's my views.py:
class LanguageViewSet(viewsets.ModelViewSet):
queryset = Language.objects.all().order_by('name')
serializer_class = LanguageSerializer
class FrameworkViewSet(viewsets.ModelViewSet):
queryset = Framework.objects.all()
serializer_class = FrameworkSerializer
class SelectedLanguageViewSet(viewsets.ModelViewSet):
queryset = Language.objects.all()
serializer_class = FrameworkSerializer
def get_queryset(self):
request = self.request
language_id = request.query_params.get('language_id')
language = Language.objects.filter(id=language_id)
self.queryset = language.framework_set.all()
return self.queryset
And my urls.py:
router = routers.DefaultRouter()
router.register(r'languages', views.LanguageViewSet)
router.register(r'frameworks', views.FrameworkViewSet)
router.register(r'language/<int:language_id>', views.SelectedLanguageViewSet)
urlpatterns = [
path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
However, only languages and frameworks work. language doesn't exists. Is this because of the get_queryset? I even tried removing the <int:language_id> in the url params but it still won't show up.
EDIT:
Forgive my naiveness, I'm quite new to django and DRF
Update
What I meant to do was like this (without DRF):
In the views.py:
def frameworks_from_language(request, language_id):
language = Language.objects.get(pk=language_id)
if language == None:
# Do some stuffs
frameworks = language.framework_set.all()
template = 'app/language.html'
context = {
'frameworks': frameworks
}
return render(request, template, context)
And in urls.py:
path('language/<int:language_id>', views.frameworks_from_language, name='getframeworks')
So the whole flow would be:
User clicks a language
Fire a get request based on the id of the selected language
Return an object of frameworks that is related to the language
This is working on a normal template-based Django. However I have no idea how to execute something like this on DRF, with all the viewsets etc.
Yes, it's because of your get_queryset (and your URL definitions).
At first, drop <int:language_id> from URL prefix, as DRF router generates the list and detail URL endpoints for you automatically. Moreover, as you're using Regex path language/<int:language_id> is taken literally (<int:language_id> has a meaning while using path, not re_path).
In your SelectedLanguageViewSet.get_queryset, you're trying to return all Framework instances related with a certain Language (you thought you would take that from language_id query param). The viewset is for Language model, and at most you should do some filtering on the default queryset inside get_queryset; absolutely don't return a whole different queryset from another model. What will happen (after fixing your URL) when you pass /language/1/? (Will 1 be a Language ID or a Framework ID ? Hint: as per you current design it would refer a Framework instance ID).
FWIW, the URL captures come as kwargs attribute in a viewset instance (i.e. self.kwargs), not via the query string.
Answer to edits:
To implement that in DRF, you can define a serializer for language with only the frameworks field:
from viewsets import ReadOnlyModelViewSet
class SelectedLanguageViewSet(ReadOnlyModelViewSet):
queryset = Language.objects.all()
serializer_class = LangaugeRelationSerializer
only list and retrieve actions should be supported, hence inherited from ReadOnlyModelViewSet.
Now, the serializer (with only one field -- frameworks):
class LangaugeRelationSerializer(serializers.ModelSerializer):
frameworks = FrameworkSerializer(source='framework_set', many=True)
class Meta:
model = Language
fields = ('frameworks',)
By default ModelSerializer sets the related fields as PrimaryKeyRelatedField, so the related objects are represented as their primary keys. If you want that instead of using the FrameworkSerializer:
class LangaugeRelationSerializer(serializers.ModelSerializer):
frameworks = serializers.PrimaryKeyRelatedField(source='framework_set', many=True)
class Meta:
model = Language
fields = ('frameworks',)
As PrimaryKeyRelatedField is the default, you can also define the source in Meta.extra_kwargs (to save you from writing the field definition yourself):
class LangaugeRelationSerializer(serializers.ModelSerializer):
class Meta:
model = Language
fields = ('frameworks',)
extra_kwargs = {
'frameworks': {
'source': 'framework_set',
'many': True,
},
}
Have you tried passing url as /language/id-of-language in the place of id-of-language pass integer i.e. the id for language in the db.
I need to understand how show or hide objects in API serialize by django rest framework.
I set a checkbox in my admin model to set active or inactive the object (true or false)
class Video(models.Model):
...
status = models.BooleanField('Activate video', default=False, help_text='If is checked show the video in the API')
...
in my urls.py i set
from video.models import Video
class VideoAPI(serializers.HyperlinkedModelSerializer):
class Meta:
model = Video
fields = [...]
class API_Video(viewsets.ModelViewSet):
queryset = Video.objects.all()
serializer_class = VideoAPI
Now how can i show or hide objects in the API Json with the checkbox in my model?
DRF uses the queryset you provide to populate the JSON response in combination with the fields you provide in serializer's Meta class.
So in order to filter out the objects with status=False you can simply filter the provided queryset:
class API_Video(viewsets.ModelViewSet):
queryset = Video.objects.filter(status=True)
serializer_class = VideoAPI
you can also call the get_queryset method and override it to do more advanced and complex filtering: DRF - Doc - get_queryset(self)
In django rest framework i am trying to add an object in a ModelSerializer with some custom functionality.
I want one of the fields to be set to self.request.user and i have the following view:
class GigSubmitView(generics.CreateAPIView):
permission_classes = (ProviderRW,)
serializer_class = serializers.GigSubmitSerializer
queryset = models.Gig.objects
def perform_create(self, serializer):
serializer.save(provider=self.request.user)
However that doesn't seem to be the correct answer. What should i do instead?
You dont need to override the perform create method to access request object in the serializer.
You have 2 options:
use serializer.context['request'] to access request object in serializer
request object is inserted by default in all generic views.
http://www.django-rest-framework.org/api-guide/serializers/#including-extra-context
you can use the current user default method to set the current user
http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
Lets say i have a model like so:
class MyModel(models.Model):
first_field = models.CharField()
second_field = models.CharField()
and an API view like so:
class MyModelDetailAPI(GenericAPIView):
serializer_class = MyModelSerializer
def patch(self, request, *args, **kwargs):
# Do the update
def post(self, request, *args, **kwargs):
# Do the post
The first_field is a field that is only inserted in the POST method (and is mandatory) but on each update, the user can't change its value so the field in the PATCH method is not mandatory.
How can i write my serializer so that the first_field is required on POST but not required on PATCH. Is there any way of dynamically setting the required field so i can still use the DRF validation mechanism? Some sort of validator dispatcher per request method?
I want something like this for example:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = {
'POST': ['first_field']
'PATCH': []
}
I need more space than comments provide to make my meaning clear. So here is what I suggest:
Different formatting means different serializers.
So here you have, for instance a MyModelSerializer and a MyModelCreationSerializer. Either create them independently, or have one inherit the other and specialize it (if it makes sense).
Use the appropriate GenericAPIView hook to return the correct serializer class depending on self.action. A very basic example could be:
class MyModelDetailAPI(GenericAPIView):
# serializer_class = unneeded as we override the hook below
def get_serializer_class(self):
if self.action == 'create':
return MyModelCreationSerializer
return MyModelSerializer
Default actions in regular viewsets are documented here, they are:
create: POST method on base route url
list: GET method on base route url
retrieve: GET method on object url
update: PUT method on object url
partial_update: PATCH method on object url
destroy: DELETE method on object url
I want to access the request object in my Views.py and Serializers.py in DRF.
My Views.py:
class ProductViewSet(viewsets.ReadOnlyModelViewSet):
"""
This viewset automatically provides `list` and `detail` actions.
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer(context={'request': request})
My Serializers.py:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
get_sr_price = serializers.SerializerMethodField('get_sr_price_func')
def get_sr_price_func(self, obj):
return self.request.user ??
class Meta:
model = Product
fields = (
'title', 'slug', 'product_stores', 'get_sr_price')
In Serializers.py I get ProductSerializer' object has no attribute 'request'. Also In views.py I get NameError: name 'request' is not defined
How do I access request object? Do I have to pass it from views to serializers? Also what's the difference between views.py and serializers.py? Generally I write all the business logic in Views.py ; here also should I do all the queries/filters in the views or should I do them in serializers or it doesn't make a difference. New to DRF please help.
You don't need to include request object in the context as the generic views passes request object to the serializer context.
DRF Source code snippet:
# rest_framework/generics.py
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request, # request object is passed here
'format': self.format_kwarg,
'view': self
}
In your serializer, you can access the request object using .context attribute.
The context dictionary can be used within any serializer field logic,
such as a custom .to_representation() method, by accessing the
self.context attribute.
class ProductSerializer(serializers.HyperlinkedModelSerializer):
get_sr_price = serializers.SerializerMethodField('get_sr_price_func')
def get_sr_price_func(self, obj):
return self.context['request'].user # access the request object
Serializers are the way external data is mapped from / to models (Django or simple Python classes).
Views are dealing with how the data will be shown. Throttling, pagination, authentication are managed by the view. They also handle the data set.
DRF provides a context to pass request specific data to the serializer without having to redefine the init. This is likely what you're looking for.