Django Rest Framework Post Nested Using Files with requests module example - django

Excuse me, i have a problem, can anyone help me.
I have models like:
from django.db import models
from django.contrib.auth.models import User
import uuid
def uuid_generator():
return str(uuid.uuid4())
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile_user')
mobile = models.CharField(max_length=20)
alamat = models.TextField(blank=True)
phone = models.CharField(max_length=20, blank=True)
foto = models.ImageField(upload_to='profile', blank=True)
def __str__(self):
return self.user.username
class Rumah(models.Model):
JENIS_CHOICES = (
('rumah', 'Rumah besar'),
('petakan', 'Rumah petakan'),
('apartement', 'Apartement'),
('kost', 'Kost-Kostan')
)
WAKTU_CHOICES = (
('bulanan', 'Bulanan'),
('tahunan', 'Tahunan'),
)
STATUS_CHOICES = (
('tersedia', 'Tersedia'),
('terisi', 'Sudah Terisi'),
('ditutup', 'Di Tutup'),
)
pemilik = models.ForeignKey(User, on_delete=models.CASCADE, related_name='rumah_user')
kode = models.CharField(unique=True, max_length=150)
jenis = models.CharField(max_length=12, choices=JENIS_CHOICES, default='rumah')
waktu = models.CharField(max_length=10, choices=WAKTU_CHOICES, default='bulanan')
harga = models.PositiveIntegerField()
alamat = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='tersedia')
keterangan = models.TextField(blank=True)
def __str__(self):
return self.kode
def save(self, *args, **kwargs):
self.kode = uuid_generator()
super(Rumah, self).save(*args, **kwargs)
class Foto(models.Model):
rumah = models.ForeignKey(Rumah, on_delete=models.CASCADE, related_name='foto_rumah')
penampakan = models.ImageField(upload_to='penampakan', blank=True)
keterangan = models.TextField(blank=True)
def __str__(self):
return self.rumah.kode
Note: Rumah like House in english and Foto like Picture in english.
I have a views like:
from django.contrib.auth.models import User
from produk.models import Profile, Rumah, Foto
from .serializers import UserSerializer, ProfileSerializer, RumahSerializer, FotoSerializer
from .permissions import IsOwnerOrReadOnly, IsHasProfile, IsRumahOrReadOnly
from rest_framework import mixins
from rest_framework import generics
from rest_framework import permissions
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class ProfileList(generics.ListCreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class ProfileDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
class RumahList(generics.ListCreateAPIView):
queryset = Rumah.objects.all()
serializer_class = RumahSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(pemilik=self.request.user)
class RumahDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Rumah.objects.all()
serializer_class = RumahSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
class FotoList(generics.ListCreateAPIView):
queryset = Foto.objects.all()
serializer_class = FotoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class FotoDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Foto.objects.all()
serializer_class = FotoSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsRumahOrReadOnly)
I have a serializers like:
from django.contrib.auth.models import User
from rest_framework import serializers
from produk.models import Profile, Rumah, Foto
class UserSerializer(serializers.HyperlinkedModelSerializer):
rumah_user = serializers.HyperlinkedRelatedField(many=True, view_name='rumah-detail', read_only=True)
profile_user = serializers.HyperlinkedRelatedField(many=False, view_name='profile-detail', read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'email', 'is_active', 'rumah_user', 'profile_user')
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = Profile
fields = ('id', 'user', 'alamat', 'mobile', 'phone', 'foto')
class FotoSerializer(serializers.ModelSerializer):
# rumah = serializers.ReadOnlyField(source='rumah.kode')
class Meta:
model = Foto
fields = ('id', 'rumah', 'penampakan', 'keterangan')
class RumahSerializer(serializers.ModelSerializer):
pemilik = serializers.ReadOnlyField(source='pemilik.username')
#foto_rumah = serializers.HyperlinkedRelatedField(many=True, view_name='foto-detail', read_only=True)
foto_rumah = FotoSerializer(many=True)
class Meta:
model = Rumah
fields = ('id', 'pemilik', 'jenis', 'waktu', 'harga', 'alamat', 'status', 'keterangan', 'foto_rumah')
def create(self, validated_data):
fotos_data = validated_data.pop('foto_rumah')
print(validated_data)
rumah = Rumah.objects.create(**validated_data)
for foto_data in fotos_data:
Foto.objects.create(rumah=rumah, **foto_data)
return rumah
When i try it with requests module like:
import requests
from requests.auth import HTTPBasicAuth
url = 'http://127.0.0.1:8000/rumah/'
files = {'penampakan': open('C:\\Users\\yanz\\Pictures\\Campuran\\test.png', 'rb')}
data = {
"pemilik": "admin",
"jenis": "rumah",
"waktu": "bulanan",
"harga": 3,
"alamat": "r",
"status": "tersedia",
"keterangan": "4",
"foto_rumah": [
{
"penampakan": files,
"keterangan": "a"
}
]
}
r = requests.post(url, data=data, files=files, auth=HTTPBasicAuth('admin', 'password123'))
print(r.text)
Result:
{"id":11,"pemilik":"admin","jenis":"rumah","waktu":"bulanan","harga":3,"alamat":"r","status":"tersedia","keterangan":"4","foto_rumah":[]}
Foto (foto_rumah) data from the model Foto is not saved. what's the solution ? How to post nested data with files?
Thanks...

Nested serializers don't work with form encoded data yet.
Uploading images only works with form encoded data out of the box.
I guess you'll need to upload images on a specific non nested entry point or use base64 encoded files within the JSON.
I remember I've seen a 3rd party about handling base64 files but can't find it again.

Related

Django rest framework "Like" functionality

in django, I want to write a function that deletes the likes if the current user has liked that post before and if so, how can I do this.
This is my models.py
class Like(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
def __str__(self):
return self.created_by.username
This is my serializers.py
class LikeSerializer(serializers.ModelSerializer):
created_by = serializers.StringRelatedField()
post = serializers.StringRelatedField()
post_id = serializers.IntegerField()
class Meta:
model = Like
fields = ('__all__')
This is my views.py
class LikesView(viewsets.ModelViewSet):
queryset = Like.objects.all()
serializer_class = LikeSerializer
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
This is my urls.py
from django.urls import path,include
from .views import (
PostView,
PostView_View,
LikesView,
CommentView
)
from rest_framework import routers
router = routers.DefaultRouter()
router.register('likes', LikesView)
router.register('post', PostView)
router.register('comment', CommentView)
urlpatterns = [
] + router.urls
I try that but still nor working
class LikeSerializer(serializers.ModelSerializer):
created_by = serializers.StringRelatedField()
post = serializers.StringRelatedField()
post_id = serializers.IntegerField()
class Meta:
model = Like
fields = ('__all__')
def post(self, request, post_id):
post = Post.objects.get(pk=post_id)
if Like.objects.filter(post=post,
created_by=request.user).exists():
Like.objects.filter(post=post, created_by=request.user).delete()
else:
Like.objects.create(post=post, created_by=request.user)
For this action, you can override the serializer's save method. First, get an object from a table, and if it exists, delete it
https://www.django-rest-framework.org/api-guide/serializers/

Create URL for detailview using DRF Modelviewset

i'm using DRF modelviewset to create an api for users model. Can anyone explain or help how can I pass a url in react-native for the detailview of user. Right now i'm getting the detailview in this manner 127.0.0.1:8000/users/users/2/. But everytime i don't want to pass the id in the url.
models.py
class User(AbstractUser):
"""This Class is used to extend the in-build user model """
ROLE_CHOICES = (('CREATOR','CREATOR'),('MODERATOR','MODERATOR'),('USERS','USERS'))
GENDER_CHOICES = (('MALE','MALE'),('FEMALE',"FEMALE"),('OTHER','OTHER'))
date_of_birth = models.DateField(verbose_name='Date of Birth', null=True)
profile_image = models.ImageField(upload_to='media/profile_images', verbose_name='Profile Image', default='media/profile_images/default.webp', blank=True)
bio = models.TextField(verbose_name='Bio')
role = models.CharField(max_length=10, verbose_name='Role', choices=ROLE_CHOICES, default='USERS')
gender = models.CharField(max_length=6, verbose_name='Gender', choices=GENDER_CHOICES)
serializers.py
class UserSerializer(serializers.ModelSerializer):
following = serializers.SerializerMethodField(read_only=True)
followers = serializers.SerializerMethodField(read_only=True)
class Meta:
model = User
fields = ('first_name','last_name','username','password','email','date_of_birth',
'profile_image','bio','role','gender', 'following','followers')
extra_kwargs = {'is_active':{'write_only':True},
'password':{'write_only':True}}
def create(self, validated_data):
logger.info('Information Stored!')
return User.objects.create_user(**validated_data)
def update(self, *args, **kwargs):
user = super().update( *args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
views.py
class UserAPI(viewsets.ModelViewSet):
serializer_class = UserSerializer
# permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
def get_queryset(self):
users = User.objects.all()
return users
urls.py
router = DefaultRouter()
router.register('users', views.UserAPI, basename='users'),
router.register('following', views.FollowingAPI, basename='following'),
urlpatterns = router.urls
How can i solve this. Need your help please. Thank you
You can make use of #action decorator.
Give this a try:
class UserAPI(viewsets.ModelViewSet):
...
#action(detail=False)
def profile(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)
Now go to 127.0.0.1:8000/users/profile/, you should see the current authenticated user's data.
this may be helpful.
You can use user = request.user.id , by in this way you can get current login user.

django-extra-views expose InlineFormSet field values into get_context_data() and forms_valid() methods

I would like to get access to fields of the InlineFormSet for consumption into the View.
Here is an example based on https://django-extra-views.readthedocs.io/en/latest/views.html#createwithinlinesview-and-updatewithinlinesview:
from extra_views import InlineFormSet, CreateWithInlinesView,
class ItemsInline(InlineFormSet):
model = Item
class TagsInline(InlineFormSet):
model = Tag
class OrderCreateView(CreateWithInlinesView):
model = Order
inlines = [ItemsInline, TagsInline]
def get_success_url(self):
return self.object.get_absolute_url()
How do I expose the InlineFormSet fields in the get_context_data() and forms_valid() methods?
Warning: Code below fails!
class OrderCreateView(CreateWithInlinesView):
[... see above ...]
def get_context_data(self, **kwargs):
context = super(OrderCreateView, self).get_context_data(**kwargs)
if self.request.POST:
context['items'] = ItemsInline(self.request.POST)
context['tags'] = TagsInline(self.request.POST)
else:
context['items'] = ItemsInline()
context['tags'] = TagsInline()
return context
def forms_valid((self, form, inlines):
[...]
return super(OrderCreateView, self).forms_valid(form, inlines)
I reproduced your example with this models:
STATUS_CHOICES = (
(0, 'Placed'),
(1, 'Charged'),
(2, 'Shipped'),
(3, 'Cancelled'),
)
class Order(models.Model):
name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
action_on_save = models.BooleanField(default=False)
class Item(models.Model):
item_name = models.CharField(max_length=255)
sku = models.CharField(max_length=13)
price = models.DecimalField(decimal_places=2, max_digits=12, db_index=True)
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
status = models.SmallIntegerField(default=0, choices=STATUS_CHOICES, db_index=True)
date_placed = models.DateField(default=now, null=True, blank=True)
def __unicode__(self):
return '%s (%s)' % (self.item_name, self.sku)
class Tag(models.Model):
tag_name = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType, null=True, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(null=True)
content_object = GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag_name
Urls:
from django.conf.urls import url
from .views import OrderCreateView, OrderUpdateView
urlpatterns = [
url(r'^inlines/new/$', OrderCreateView.as_view()),
url(r'^inlines/(?P<pk>\d+)/$', OrderUpdateView.as_view()),
]
Forms:
from django import forms
from .models import Order, Item
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['name']
def save(self, commit=True):
instance = super(OrderForm, self).save(commit=commit)
if commit:
instance.action_on_save = True
instance.save()
return instance
class ItemForm(forms.ModelForm):
flag = forms.BooleanField(initial=True)
class Meta:
model = Item
fields = ['item_name', 'sku', 'price', 'order', 'status']
Views:
from django.contrib.contenttypes.models import ContentType
from extra_views import InlineFormSet, CreateWithInlinesView, UpdateWithInlinesView
from extra_views.generic import GenericInlineFormSet
from .forms import OrderForm
from .models import Item, Order, Tag
class ItemsInline(InlineFormSet):
model = Item
fields = ['item_name', 'sku', 'price', 'order', 'status']
class TagsInline(GenericInlineFormSet):
model = Tag
fields = ['tag_name']
class OrderCreateView(CreateWithInlinesView):
model = Order
fields = ['name']
context_object_name = 'order'
inlines = [ItemsInline, TagsInline]
template_name = 'extra_views/order_and_items.html'
def get_success_url(self):
return '/inlines/%i' % self.object.pk
class OrderUpdateView(UpdateWithInlinesView):
model = Order
form_class = OrderForm
inlines = [ItemsInline, TagsInline]
template_name = 'extra_views/order_and_items.html'
def get_success_url(self):
return ''
In OrderCreateView you cannot read Tag and Item records in the
get_context_data method because they haven't been created.
In OrderCreateView you can read all records after the forms_valid
method as usual.
In OrderUpdateView you can read all records in the
get_context_data method like in the forms_valid method.
It looks like this:
class OrderCreateView(CreateWithInlinesView):
# ...
def get_context_data(self, **kwargs):
data = super(OrderCreateView, self).get_context_data(**kwargs)
from pprint import pprint
pprint(self.request.POST) # there is only post data here
return data
def forms_valid(self, form, inlines):
instance = super(OrderCreateView, self).forms_valid(form, inlines)
ct = ContentType.objects.get_for_model(self.model)
print('all items', [item.item_name for item in self.object.items.all()]) # items
print('all tags', [tag.tag_name for tag in
TagsInline.model.objects.filter(content_type=ct, object_id=self.object.id)]) # tags
return instance
class OrderUpdateView(UpdateWithInlinesView):
# ...
def get_context_data(self, **kwargs):
context = super(OrderUpdateView, self).get_context_data(**kwargs)
ct = ContentType.objects.get_for_model(self.model)
print('all items', [item.item_name for item in self.object.items.all()]) # items
print('all tags', [tag.tag_name for tag in
TagsInline.model.objects.filter(content_type=ct, object_id=self.object.id)]) # tags
return context

How to set items to a specific Todolist in Django Rest Framework?

Im building a TODO app using django rest framework. I have to two models namely todolist and todoitem.How can I set a new todoitem to a specific todolist? I tried using foreign key but it didn't worked out,Sorry for my bad english,Can anyone please help me
My models.py:
class todolist(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE,default=None)
name = models.CharField(max_length=128)
creation_date = models.DateField(auto_now=True)
def __unicode__(self):
return self.name
class todoitem(models.Model):
description = models.CharField(max_length=150)
completed = models.BooleanField(default=False)
due_by = models.DateField(auto_now=True)
parent = models.ForeignKey(todolist,on_delete=models.CASCADE)
def _unicode__(self):
return self.description
My classviews.py:
class Todolist(LoginRequiredMixin,ListCreateAPIView):
serializer_class = listserializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def get_queryset(self):
self.queryset = todolist.objects.filter(user=self.request.user)
return super(Todolist, self).get_queryset()
class Todolistdetail(LoginRequiredMixin,RetrieveUpdateDestroyAPIView):
def get_queryset(self):
self.queryset = todolist.objects.filter(user=self.request.user)
return super(Todolistdetail, self).get_queryset()
serializer_class = listserializer
class Todoitem(LoginRequiredMixin,ListCreateAPIView):
def perform_create(self, serializer):
temp = self.queryset(todolist.objects.filter(id=self.kwargs['pk']))
serializer.save(parent=temp)
def get_queryset(self):
temp = todolist.objects.filter(id=self.kwargs['pk'])
self.queryset = todoitem.objects.filter(parent=temp)
return super(Todoitem, self).get_queryset()
serializer_class = itemserializer
class Todoitemdetail(LoginRequiredMixin,RetrieveUpdateDestroyAPIView):
def get_queryset(self):
temp = todolist.objects.filter(id=self.kwargs['list_id'])
self.queryset = todoitem.objects.filter(parent=temp)
return super(Todoitemdetail, self).get_queryset()
serializer_class = itemserializer
My serializers.py:
class listserializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = todolist
fields = '__all__'
class itemserializer(serializers.ModelSerializer):
parent = serializers.ReadOnlyField(source='todolist.id')
class Meta:
model = todoitem
fields = '__all__'
My urls.py:
from todoapp import classviews
app_name = "todoapp"
urlpatterns = [
url(r'^list/$',classviews.Todolist.as_view(),name="list_lists"),
url(r'^list/(?P<pk>[0-9]+)/$',classviews.Todolistdetail.as_view(),name="each_list"),
url(r'^list/(?P<pk>[0-9]+)/item/$',classviews.Todoitem.as_view(),name="list_items"),
url(r'^list/(?P<list_id>[0-9]+)/item/(?P<pk>[0-9]+)/$',classviews.Todoitemdetail.as_view(),name="each_item")
]
view:
from rest_framework.viewsets import ModelViewSet
class TodoitemViewSet(ModelViewSet):
queryset = Todoitem.objects.all()
serializer_class = itemserializer
permission_classes = (IsAuthenticated)
serializer.py
class itemserializer(serializers.ModelSerializer):
class Meta:
model=todoitem
fields='__all__'
url:
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'todo_item', TodoitemViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include(router.urls)),
]
jsut post data {'parent':1 'description':'example',....} to www.example.com/todo_item/, parent is your todolist'id.

TypeError: __init__() got multiple values for keyword argument 'view_name'

I am using the Django Rest Framework, and I am not sure why I am getting this error.
models.py
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
followers = models.ManyToManyField('self', related_name='followees', symmetrical=False)
class Post(models.Model):
author = models.ForeignKey(User, related_name = 'posts')
title = models.CharField(max_length = 255)
body = models.TextField(blank = True, null = True)
class Photo(models.Model):
post = models.ForeignKey(Post, related_name = 'photos')
image = models.ImageField(upload_to = '%Y/%m/%d')
serializers.py
from rest_framework import serializers
from .models import *
class UserSerializer(serializers.ModelSerializer):
# Getting the list of posts made by particular users using the username.
posts = serializers.HyperlinkedIdentityField(
'posts',
view_name = 'userpost-list',
lookup_field = 'username'
)
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'posts',)
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required = False)
photos = serializers.HyperlinkedIdentityField(
'photos',
view_name = 'postphoto-list'
)
def get_validated_exclusions(self):
# Need to exclude 'author' since we'll add that later
# based off the request user
exclusions = super(PostSerializer, self).get_validated_exclusions()
return exclusions + ['author']
class Meta:
model = Post
class PhotoSerializer(serializers.ModelSerializer):
image = serializers.Field('image.url')
class Meta:
model = Photo
views.py
from rest_framework import generics, permissions
from .serializers import *
from .models import *
class UserList(generics.ListCreateAPIView):
model = User
serializer_class = UserSerializer
permission_classes = [
permissions.AllowAny # Publically available to anyone
]
class UserDetail(generics.RetrieveAPIView):
model = User
serializer_class = UserSerializer
lookup_field = 'username'
class PostList(generics.ListCreateAPIView):
model = Post
serializer_class = PostSerializer
permission_classes = [
permissions.AllowAny
]
class PostDetail(generics.RetrieveAPIView):
model = Post
serializer_class = PostSerializer
permission_classes = [
permissions.AllowAny
]
class UserPostList(generics.ListAPIView):
"""
Lists all the posts of a particular User.
"""
model = Post
serializer_class = PostSerializer
def get_queryset(self):
queryset = super(UserPostList, self).get_queryset()
return queryset.filter(author__username = self.kwargs.get('username'))
class PhotoList(generics.ListCreateAPIView):
model = Photo
serializer_class = PhotoSerializer
permission_classes = [
permissions.AllowAny
]
class PhotoDetail(generics.RetrieveAPIView):
model = Photo
serializer_class = PhotoSerializer
permission_classes = [
permissions.AllowAny
]
class PostPhotoList(generics.ListAPIView):
model = Photo
serializer_class = PhotoSerializer
def get_queryset(self):
queryset = super(PostPhotoList, self).get_queryset()
return queryset.filter(post__pk = self.kwargs.get('pk'))
urls.py in my app directory
from django.conf.urls import patterns, url, include
from .views import *
urlpatterns = [
# User URLs
url(r'^users/$', UserList.as_view(), name='user-list'),
url(r'^users/(?P<username>[0-9a-zA-Z_-]+)/$', UserDetail.as_view(), name='user-detail'),
url(r'^users/(?P<username>[0-9a-zA-Z_-]+)/posts/$', UserPostList.as_view(), name='userpost-list'),
# Post URLs
url(r'^posts/$', PostList.as_view(), name='post-list'),
url(r'^posts/(?P<pk>\d+)/$', PostDetail.as_view(), name='post-detail'),
url(r'^posts/(?P<pk>\d+)/photos/$', PostPhotoList.as_view(), name='postphoto-list'),
# Photo URLs
url(r'^photos/$', PhotoList.as_view(), name='photo-list'),
url(r'^photos/(?P<pk>\d+)/$', PhotoDetail.as_view(), name='photo-detail'),
]
When I try to run the check command on my terminal, or runserver, I get this error:
TypeError: init() got multiple values for keyword argument 'view_name'
What am I doing wrong exactly, and how can I fix this problem?
The first argument to HyperlinkedIdentityField is view_name. You're passing an extra initial argument, which seems to be the same as the field name; remove this argument.