DRF - html post options only for user logged in - django-rest-framework

I have the following serializer:
class WidgetSerializer(serializers.ModelSerializer):
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model=Widget
fields = ('id', 'title', 'description', 'username', 'code', 'owner', 'list')
The problem is that the 'list' field, which is a drop down, gives all lists whereas I only want it to display lists that are owned by the user currently logged in.
Here's the respective models:
class WidgetList(MPTTModel):
name = models.CharField(max_length=100)
description = models.CharField(max_length=1024)
owner = models.ForeignKey('MyUser')
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
def __str__(self):
return self.name
class MPTTMEta:
order_insertion_by = ['name']
class Widget(models.Model):
title = models.CharField(max_length=100)
description = models.CharField(max_length=1024)
username = models.CharField(max_length=50)
code = models.CharField(max_length=1024)
owner = models.ForeignKey('MyUser', related_name='MyUser_owner')
list = models.ForeignKey('WidgetList')

I am a beginner in django. I hope that I could help.
Just try this
WidgetList.objects.filter(owner=request.user)

I have to limit it through a SlugRelatedField as per the documentation here -
http://www.django-rest-framework.org/api-guide/relations/#slugrelatedfield
I then used it like so -
list = serializers.SlugRelatedField(
queryset=WidgetList.objects.filter(owner=3),
many=True,
slug_field='name'
)
All I need to figure out now is to pass the serializers.CurrentUserDefault() in the filter for the queryset, or pass request.user.

Related

AttributeError: 'collections.OrderedDict' object has no attribute 'pk' with Django Rest Framework while adding item to a many to many relationship

First question here, so please bear with me. I'm learning Django and the Django Rest Framework, and I'm getting an exception when adding a related object in a many to many relationship. What I do not understand is that the object gets created correctly, and I am getting the error when getting the response.
This is an excerpt of my urls.py:
path('mybox/', views_api.MyBoxList.as_view(), name='mybox-list'),
path('mybox/<int:pk>/', views_api.MyBoxDetail.as_view(), name='mybox-detail'),
path('mybox/<int:pk>/documents/', views_api.DocumentList.as_view(), name='mybox-document-list'),
path('documents/', views_api.DocumentList.as_view(), name='document-list'),
path('documents/<int:pk>/', views_api.DocumentDetail.as_view(), name='document-detail'),
My models.py:
class MyBox(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='boxes')
name = models.CharField(_('name'), max_length=100, default='my_box')
description = models.CharField(_('description'), max_length=250, null=True, blank=True)
time_created = models.DateTimeField(_('time created'), auto_now_add=True)
def __str__(self):
return self.name
def user_folder_doc_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/docs/<filename>
return 'user_{0}/docs/{1}'.format(instance.owner.id, filename)
class Document(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='documents')
file = models.FileField(_('file'), upload_to=user_folder_doc_path, null=True, blank=True)
description = models.TextField(_('description'), null=True, blank=True)
time_created = models.DateTimeField(_('time created'), auto_now_add=True)
mybox = models.ManyToManyField(MyBox, related_name='documents', blank=True)
My serializers:
class MyBoxSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
class Meta:
model = MyBox
fields = ['url', 'id', 'user', 'name', 'description', 'documents']
class DocumentSerializer(serializers.HyperlinkedModelSerializer):
mybox = MyBoxSerializer(many=True, read_only=True)
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Document
fields = ['url', 'id', 'owner', 'description', 'file', 'mybox']
extra_kwargs = {'mybox': {'required': False}}
And my views.py:
class DocumentList(generics.ListCreateAPIView):
#queryset = Document.objects.all()
serializer_class = DocumentSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
"""
This view should return a list of all the documents
for the currently authenticated user.
"""
owner = self.request.user
mybox_id = self.kwargs.get('pk', None)
if mybox_id:
return Document.objects.filter(mybox=mybox_id, owner=owner)
else:
return Document.objects.filter(owner=owner)
def perform_create(self, serializer):
mybox_id = self.kwargs.get('pk', None)
if mybox_id:
print("Hay ID de MyBox")
mybox = MyBox.objects.get(pk=mybox_id)
serializer = DocumentSerializer(data=self.request.data)
if serializer.is_valid():
document = serializer.save(owner=self.request.user)
mybox.documents.add(document)
else:
print("No hay ID de MyBox")
serializer.save(owner=self.request.user)
I've tried several suggestions I found online, with no luck. I believe the issue is my serializers, and I tried adding this to MyBoxSerializer: documents = serializers.HyperlinkedIdentityField(view_name='mybox-document-list', lookup_field='mybox_id')
But I think I do not understand serializers enough when dealing with M2M relationships.
Just to reiterate, after going to mybox/1/documents/ and creating a new document here, the document gets properly created and the relationship with MyBox id:1 (in this example) is also there, but I never get a response, only the exception
Thanks for any help/suggestions!
So I finally managed to fix the issue, thanks to the solution provided by somebody else. In case anyone gets to this page with a similar issue.
The problem was in the perform_create function, which had a serializer too many and I had to save the document too. This is the correct version that works for me:
def perform_create(self, serializer):
mybox_id = self.kwargs.get('pk', None)
if mybox_id:
mybox = MyBox.objects.get(pk=mybox_id)
if serializer.is_valid():
document = serializer.save(owner=self.request.user)
document.mybox.add(mybox)
document.save()
else:
serializer.save(owner=self.request.user)
I hope this helps

Either provide a url or define a get_absolute_url method on the Model

Trying to have the user who wants to update a product that they already created, to be able to update and be redirected to a page.
HERE IS THE ERROR I'M RUNNING INTO:
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'updatelisting' not found. 'updatelisting' is not a valid view function or pattern name.
HERE IS MY FORMS.PY FILE:
class ProductForm(ModelForm):
class Meta:
model = listing
fields = '__all__'
HERE IS MY VIEWS.PY FILE WITH THE RELATED VIEW:
from django.views.generic import UpdateView
class UpdateListing(UpdateView):
model = listing
template_name = 'update_listing.html'
fields = '__all__'
HERE IS MY URLS.PY FILE WITH THE RELATED URLS TO THE RELATED VIEWS:
I have set the urls main name to vendor.
app_name = 'vendor'
path('createlisting/', views.index, name='index'),
path('renderindex/', views.renderindex, name='renderindex'),
path('listing/edit/<int:pk>', views.UpdateListing.as_view(), name='updatelisting'),
HERE IS MY MODELS.PY FILE WITH THE RELATED MODEL TO THIS PROBLEM:
class listing(models.Model):
image = models.ImageField(blank=True, null=True)
name = models.CharField(max_length=255)
description = models.TextField()
unit_price = models.DecimalField(max_digits=6, decimal_places=2, validators=[MinValueValidator(1)])
inventory = models.IntegerField()
last_update = models.DateTimeField(auto_now=True)
collection = models.ForeignKey(Collection, on_delete=models.PROTECT, blank=True, null=True)
vendors = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=False)
def get_absolute_url(self):
return reverse('updatelisting', args=(str(self.id)))
It's funny because when I refresh the page it then says that the 'updatelisting' is not a pattern name or view name but it is a pattern name, I'm not sure why it is not identifying the pattern name which is ( updatelisting ). If you need anymore info let me know please. I thankyou for the help!

DRF - Add User to a model with ManyToManyField

I am trying to implement a feature to my backend and allow the owner of private "Group" to add other users by their usernames instead of ID's and allow them to add their images to FileField only once after they were added to the model. The code I have so far:
models.py
class Group(models.Model):
group_name = models.CharField(max_length=255)
group_text = models.TextField(max_length=360, blank=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='owner_user', on_delete=models.SET(get_deleted_user), default=1)
created_on = models.DateTimeField(auto_now_add=True, null=True)
shared_to = models.ManyToManyField(UserProfile, blank=True, related_name='shared_to_user', null=True)
def __str__(self):
return self.group_name
def save(self, *args, **kwargs):
super(Group, self).save(*args, **kwargs)
class GroupImage(models.Model):
group_file = models.FileField(blank=True, null=True,
upload_to='media/covers/%Y/%m/%D/')
gallery_group = models.ForeignKey(Group, related_name='images', on_delete=models.CASCADE)
serializers.py
class GroupImageSerializer(serializers.ModelSerializer):
class Meta:
model = models.GroupImage
fields = ('group_file', )
class SharedToSerializer(serializers.ModelSerializer):
class Meta:
model = models.Group
fields = ('shared_to', )
class GroupSerializer(serializers.ModelSerializer):
images = GroupImageSerializer(many=True, read_only=True)
person = SharedToSerializer(many=True, read_only=True)
class Meta:
model = models.Group
fields = ('id', 'group_name', 'group_text', 'person', 'images')
def create(self, validated_data):
images_data = self.context.get('view').request.FILES
owner_id = self.context['request'].user.id
gallery_group = models.Group.objects.create(group_name=validated_data.get('group_name', 'no-
group_name'), group_text=validated_data.get('group_text'), owner_id=1)
for image_data in images_data.values():
models.GroupImage.objects.create(gallery_group=gallery_group,
group_file=image_data)
return gallery_group
views.py
class GroupCreateAPIView(generics.CreateAPIView):
queryset = models.Group.objects.all()
serializer_class = serializers.GroupSerializer
permission_classes = [AllowAny]
So if your only requirement is how to add users by their username and not their id. You should use SlugRelatedField. I also feel your serializer naming convention is quite confusing. Below is the serializer for Group model that can add users to a group.
class GroupSerializer(Serializer):
... other fields here
shared_to = models.SlugRelatedField(queryset = UserProfile.objects.all(), many=True, slug_field="username", allow_empty=True)
So first checkout SlugRelatedField. This basically is used to map to objects using a specific field of that object(username in this case). You will then get all the UserProfile instances in the shared_to field of the validated_data
property of the serializer which you can fetch in create method and add to you group. And then in the file upload api for your group you can check whether this user belongs to the group or not for permission checking.

Saving(create,update) along with foreignkey value from another model which is related user model

It may be a challenging question if you didn't get rightly. Here I have three models in which department model should be created by taking its place name from Place model which is related to the staff model. The Staff Model is in a OneToOneField relationship with User, so when a user creates a department the place name should be passed like HiddenField in HTML . This place name is related to place model with the user with GenericForeignKey. i have created a serializer which is not working as expected, it is returning the place name ,
.
In shortly I want to create a department while place should be selected from current user ID
class Staff(BaseModel):
ROLES = [
('ADMIN', 'Admin'),
('TEACHER', 'Teacher')
]
auth_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
school_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
school_id = models.PositiveIntegerField()
school = GenericForeignKey('school_content_type', 'school_id')
role = models.CharField(null=True, blank=True, choices=ROLES, max_length=20)
class Places(BaseModel):
name = models.CharField(max_length=50)
code = models.CharField(max_length=12, unique=True)
class Department(BaseModel):
TYPES = [
('ONLINE', 'Online'),
('OFFLINE', 'OfFline')
]
department_type = models.CharField(max_length=15, choices=TYPES)
service_no = models.CharField(max_length=50)
instructions = models.TextField(null=True, blank=True)
place = models.ForeignKey(Places, to_field='code', db_column='place_code', on_delete=models.PROTECT)
SERIALIZERS
class DepartmentCreateSerializer(serializers.ModelSerializer):
place_code=serializers.CharField(read_only=True)
class Meta:
model=Department
fields = ('department_type','service_no','instructions')
def get_place(self, request):
user_id=self.context['request'].user.id
school_id=Staff.objects.get(auth_user_id= user_id).school_id
places_code_name=Places.objects.get(id= school_id).name
class PlacesSerializer(serializers.ModelSerializer):
class Meta:
model = Places
fields = ('id', 'code', 'name')
from places.serializers import PlacesSerializer
class DepartmentCreateSerializer(serializers.ModelSerializer):
place= PlacesSerializer(read_only=True)
class Meta:
model=Department
fields = ('place','service_no','instructions')
def validate(self, attrs):
palce_obj = self.context['request'].user.staff.place()
attrs.update({'place': place_obj})
attrs = super().validate(attrs)
if not attrs.get('place', None):
raise serializers.ValidationError({'place': ["Place required"]})
return attrs

Handling viewset that has a serializer with multiple foreignkeys

I have this serializer:
class ReviewSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Review
fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'review_text',)
I want to mention that user and movie are both ForeignKeys defined in models.py for Review model. Here is the model:
class Review(models.Model):
title = models.CharField(max_length=255)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='reviews')
review_text = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{movie} review by {user}'.format(user=self.user, movie=self.movie)
I want to create a viewset, but if I do something like this:
class ReviewsViewSet(viewsets.ModelViewSet):
queryset = Movie.objects.all()
serializer_class = ReviewSerializer
and register it like this:
router.register(r'reviews', views.ReviewsViewSet, 'reviews')
won't work, it requires me to provide a movie field as logged here:
AttributeError at /api/reviews/
Got AttributeError when attempting to get a value for field `movie` on serializer `ReviewSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Movie` instance.
Original exception text was: 'Movie' object has no attribute 'movie'.
Thanks in advance.
Edited.

Resources