I've custom CBV delete view which should check object's owner against request.user.
What is the good practice to do it?
Currently I've been checking as shown below
equal_usernames = some_object.user.username in request.user.username
equal_emails = some_object.user.email in request.user.email
if equal_usernames and equal_emails:
# some actions
Sultan
As simple as:
some_object.user == request.user
Because Model as __eq__ defined as such:
def __eq__(self, other):
return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()
So it's "equal" as long as it's the same model class with the same pk.
Related
I'm looking for a way to use different serializers within a ModelViewSet depending on the request.user properties making the call.
Case 1:
The request.user is the owner of the profile and must use the serializer called 'UserProfileOwnerSerializer' which allows a partial edit of their properties.
Case 2:
request.user has full control rights over profiles properties and must therefore use 'UserProfileViewEditSerializer'
Case 3:
request.user has only read rights on user profiles and must use 'UserProfileViewOnlySerializer' which sets all fields to readonly.
I created 3 permission checkers also used to check permissions within 'permissions.BasePermission':
def haveProfileOwnerRights(request, obj):
if (request.user.userprofile.id == obj.id):
return True
else:
return False
def haveProfileViewRights(request):
roleRightsProfileView = [
'MA',
'AM',
'ME',
'VI',
]
role = request.user.userprofile.role
if (role in roleRightsProfileView):
return True
else:
return False
def haveProfileViewEditRights(request):
roleRightsProfileViewEdit = [
'MA',
'AM',
'ME',
]
role = request.user.userprofile.role
if (role in roleRightsProfileViewEdit):
return True
else:
return False
class IsOwnerOrHaveProfileViewEditOrViewRight(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if (request.user.is_anonymous):
return False
if (haveProfileOwnerRights(request, obj)):
return True
if (haveProfileViewEditRights(request)):
return True
return False
class UserProfileViewSet(viewsets.ModelViewSet):
permission_classes = [
permissions.IsAuthenticated, IsOwnerOrHaveProfileViewEditOrViewRight
]
queryset = UserProfile.objects.all()
def get_serializer_class(self):
if haveProfileViewEditRights(self.request):
return UserProfileViewEditSerializer
if haveProfileViewRights(self.request):
return UserProfileViewOnlySerializer
#
# MISSING SERIALIZERS FOR 'UserProfileOwnerSerializer'
# I need to know here the content of the object to be serialized
#
To check if the serializer that I have to use for users who have 'haveProfileOwnerRights' I must be able to know the content of the object in order to pass it as a parameter to the 'haveProfileOwnerRights' function.
How can I get the object to be serialized inside 'get_serializer_class'?
Or is there a different approach that allows me to achieve the same result but in a different way?
Please save my brain :-)
You can override get_serializer(). It should receive the instance as the first argument.
class UserProfileViewSet(viewsets.ModelViewSet):
def get_serializer(self, instance=None, *args, **kwargs):
if instance.type == "xxx":
serializer_class = # set it depending on your conditions
else:
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(instance, *args, **kwargs)
I'm using the CreateView of Django and I'm trying to find out how I can modify any text which gets sent before it gets saved. For example, right now I'm only looking to lowercase all the text before saving.
I know I need to use form_valid() but I can't seem to get it right.
forms.py
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
fields = ["heading", "name", "data", "rating"]
views.py
def form_valid(self, form):
super().form_valid(form)
form.fields["heading"].lower()
form.fields["name"].lower()
form.fields["data"].lower()
That shouldn't be done in form_valid. You should do that in the form itself. Instead of letting CreateView automatically create a form for you, do it explicitly and overwrite the clean method.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('list', 'of', 'fields')
def clean(self):
for field, value in self.cleaned_data.items():
self.cleaned_data['field'] = value.lower()
...
class MyCreateView(views.CreateView):
form_class = MyForm
Override get_form_kwargs method to update the kwargs which instantiates the form.
Solution:
def get_form_kwargs(self):
# update super call if python < 3
form_kwargs = super().get_form_kwargs()
form_kwargs['data']['str_field_name'] = form_kwargs['data']['str_field_name'].lower()
return form_kwargs
Ref:
get_form_kwargs docs
While it may not be the nicest solution, it can be done like this:
def form_valid(self, form):
self.object = form.save(commit=False)
# ...
self.object.save()
return http.HttpResponseRedirect(self.get_success_url())
Just for the record
In the first case
def get_form_kwargs(self):
# update super call if python < 3
form_kwargs = super().get_form_kwargs()
form_kwargs['data']['str_field_name'] = form_kwargs['data'['str_field_name'].lower()
return form_kwargs
Django complains "This QueryDict instance is immutable". And workaround is
data = kwargs['data'].copy() # mutable copy
data['foo'] = 'whatever' #supply the missing default value
kwargs['data'] = data
Is there some elegant solution to using Django's DeleteView but instead actually deleting the objects, marking them inactive? I like the simplicity of the DeleteView but would want to keep the data at the backend, instead of removing it.
Elegant solution would be overriding Model & Manager to update a field on delete. This is an implementation as Abstract Model, so that it can be extended by any other Model. You can modify it as per your need, if you already have delete fields in your model.
Soft Deletion Abstract Model
class SoftDeletionModel(models.Model):
deleted_at = models.DateTimeField(blank=True, null=True)
objects = SoftDeletionManager()
all_objects = SoftDeletionManager(alive_only=False)
class Meta:
abstract = True
def delete(self):
self.deleted_at = timezone.now()
self.save()
def hard_delete(self):
super(SoftDeletionModel, self).delete()
Object Manager
class SoftDeletionManager(models.Manager):
def __init__(self, *args, **kwargs):
self.alive_only = kwargs.pop('alive_only', True)
super(SoftDeletionManager, self).__init__(*args, **kwargs)
def get_queryset(self):
if self.alive_only:
return SoftDeletionQuerySet(self.model).filter(deleted_at=None)
return SoftDeletionQuerySet(self.model)
def hard_delete(self):
return self.get_queryset().hard_delete()
QuerySet
class SoftDeletionQuerySet(QuerySet):
def delete(self):
return super(SoftDeletionQuerySet, self).update(deleted_at=timezone.now())
def hard_delete(self):
return super(SoftDeletionQuerySet, self).delete()
def alive(self):
return self.filter(deleted_at=None)
def dead(self):
return self.exclude(deleted_at=None)
For explanation, see Soft Deletion in Django
The DeleteView inherits DeletionMixin so you can just predefine the delete method.
DeletionMixin
Override delete method in DeleteView as follows
class Example(DeleteView):
def delete(self, request, *args, **kwargs):
"""
Calls the delete() method on the fetched object and then
redirects to the success URL.
"""
self.object = self.get_object()
self.object.is_deleted = True # Declare a boolean field is_deleted in your model. Default value is Flase.
return HttpResponseRedirect(self.get_success_url())
Here is my serializer method field:
def get_is_user_to_user(self, obj):
return obj.to_user == self.context.get('request').user.id
I want the method to return a boolean value of True if obj.to_user which is a field in the corresponding model equals the request.user.id. The method field at the moment always returns False even when it should be returning True.
How do I check if obj.to_user is equal to the request.user.id from the serializer method field?
def get_is_user_to_user(self, obj):
return obj.to_user == self.context.get('request').user.id
I think your FK obj.to_user is a User instance, you can not compare with self.context.get('request').user.id.
Your code should:
def get_is_user_to_user(self, obj):
return obj.to_user.id == self.context.get('request').user.id
Or:
def get_is_user_to_user(self, obj):
return obj.to_user == self.context.get('request').user # Make sure you did not override request.user before.
I used below Sample View
class CurrentUserView(APIView):
def get(self, request):
serializer = UserSerializer(request.user)
return Response({"user": serializer.data})
You can define a method i user model then use at as a field
E.g.:
class User(AbstractUser):
def chechsomething(self):
if something :
return True
return False
I'm using the Python libraries for Google App Engine. How can I override the equals() method on a class so that it judges equality on the user_id field of the following class:
class UserAccount(db.Model):
# compare all equality tests on user_id
user = db.UserProperty(required=True)
user_id = db.StringProperty(required=True)
first_name = db.StringProperty()
last_name = db.StringProperty()
notifications = db.ListProperty(db.Key)
Right now, I'm doing equalty by getting a UserAccount object and doing user1.user_id == user2.user_id. Is there a way I can override it so that 'user1 == user2' will look at only the 'user_id' fields?
Thanks in advance
Override operators __eq__ (==) and __ne__ (!=)
e.g.
class UserAccount(db.Model):
def __eq__(self, other):
if isinstance(other, UserAccount):
return self.user_id == other.user_id
return NotImplemented
def __ne__(self, other):
result = self.__eq__(other)
if result is NotImplemented:
return result
return not result