In django, creating a User has a different and unique flow from the usual Model instance creation. You need to call create_user() which is a method of BaseUserManager.
Since django REST framework's flow is to do restore_object() and then save_object(), it's not possible to simply create Users using a ModelSerializer in a generic create API endpoint, without hacking you way through.
What would be a clean way to solve this? or at least get it working using django's built-in piping?
Edit:
Important to note that what's specifically not working is that once you try to authenticate the created user instance using django.contrib.auth.authenticate it fails if the instance was simply created using User.objects.create() and not .create_user().
Eventually I've overridden the serializer's restore_object method and made sure that the password being sent is then processes using instance.set_password(password), like so:
def restore_object(self, attrs, instance=None):
if not instance:
instance = super(RegisterationSerializer, self).restore_object(attrs, instance)
instance.set_password(attrs.get('password'))
return instance
Thanks everyone for help!
Another way to fix this is to overwrite pre_save(self, obj) method in your extension of viewsets.GenericViewSet like so:
def pre_save(self, obj):
""" We have to encode the password in the user object that will be
saved before saving it.
"""
viewsets.GenericViewSet.pre_save(self, obj)
# Password is raw right now, so set it properly (encoded password will
# overwrite the raw one then).
obj.user.set_password(obj.user.password)
Edit:
Note that the obj in the code above contains the instance of User class. If you use Django's user model class directly, replace obj.user with obj in the code (the last line in 2 places).
I'm working with DRF. And here is how I create users:
I have a Serializer with overrided save method:
def save(self, **kwargs ):
try:
user = create_new_user(self.init_data)
except UserDataValidationError as e:
raise FormValidationFailed(e.form)
self.object = user.user_profile
return self.object
create_new_user is just my function for user creation and in the view, I just have:
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
It seems like you should be overriding restore_object() in your serializer, not save(). This will allow you to create your object correctly.
However, it looks like you are trying to abuse the framework -- you are trying to make a single create() create two objects (the user and the profile). I am no DRF expert, but I suspect this may cause some problems.
You would probably do better by using a custom user model (which would also include the profile in the same object).
Related
I want to filter the user manager self.get_queryset() method in such a way that users on the client application don't see admin and staff users when searching for or viewing other accounts. The issue I'm running into is I am unable to login with my auth system if I override get_queryset entirely. My current setup is:
class AccountManager(BaseUserManager):
def get_public_queryset(self):
return self.get_queryset().filter(active=True, verified=True, admin=False, staff=False)
Using this design works fine if I define various sorting methods in the manager (because I can simply call that method), but it seems as though there should be a better way to do this. Any ideas?
I think additional methods, as you've implemented is good a solution, but if you insist using get_queryset method it would be fine to override the method and preserve base functionality. I'd do something like this:
...
def get_queryset(self, *a, **kw):
queryset = super().get_queryset(*a, **kw)
# filter your queryset here as you wish
queryset = queryset.filter(active=True, verified=True, admin=False, staff=False)
return queryset
...
As I've spotted from question text, you tried to call self.get_queryset() which would be recursive call (not super class implementation call), which will finally cause maximum recursion depth exceeded error.
Hope it helps
I have been learning the Django framework for 3-4 months but there is 1 thing that bugs me the most and I am unable to find a satisfactory answer yet.When we define the functions/methods inside a Class based views, do they get called automatically when some object is created? Like we use
#action(detail=False)
def recent_users(self, request):
recent_users = User.objects.all().order_by('-last_login')
page = self.paginate_queryset(recent_users)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
How this method will be executed? DO we have to make an object and call like obj.method?
And how those get() get_queryset() methods work given with the Class Based Views?? How do they process the data from models?
and one last question would be the mixture of these both.
Can I create a new method inside a ClassBasedView? If yes, How do I execute it ? Say in return and render data inside a template ?
Please do not provide any link. I just want to know in simple English terms. If anybody knows, it'll be very helpful.
When referencing a class based view in the path (url prior to 2.X) function we call the as_view class method. Looking at the source code will show that this essentially defines a function view that calls a class based view's dispatch method.
What this means is that the entry point in a class based view is the dispatch method. All other methods are called somewhere down the line from dispatch. The methods immediately called by dispatch are all going to be named according to HTTP methods (E.G. get, post, delete).
This is the typical path for a view inheriting from TemplateView.
as_view returns a function that calls dispatch.
dispatch calls get or, if request.method isn't GET, the http_method_not_allowed method.
get calls get_context_data and passes that as an argument to render_to_response.
render_to_response calls get_template_names and passes that as an argument to TemplateResponse.
You can define any method you want on your class based view, but it won't be called unless you call it somewhere that is already being called.
One common modification is to add something to get_context_data.
def get_context_data(self, **kwargs):
kwargs.setdefault('recent_users', self.recent_users())
return super().get_context_data(**kwargs)
def recent_users(self):
# self.request is accessible here.
...
https://ccbv.co.uk/ is a helpful resource for writing class based views. I reference that site all the time while I'm writing class based views.
In a Django model, I want to avoid doubles so I wrote this:
class Points(TimeStampedModel):
....
def save(self, *args, **kwargs):
if self.pk:
super(Points, self).save(*args, **kwargs)
else: # save() is a creation here, not an update
if Points.objects.filter(benef_card=self.benef_card,
spendable_at=self.spendable_at).exists():
pass
else:
super(Points, self).save(*args, **kwargs)
I was very surprised to find this result in my database:
I suppose there is something wrong with my code, but I'd like to know how these doubles could exist inspite of the protection I wrote in my save() method?
I think what you want instead is:
class Points(TimeStampedModel):
# ...
class Meta:
unique_together = ('benef_card', 'spendable_at')
Then you don't need to override save -- the uniqueness will be handled by a DB constraint and it is generally the way to go. This approach is better because save is not always called (example: bulk operations) so you might get different behavior across your app.
You might also want to check out update_or_create which just returns an object with attributes you need, creating it if it doesn't exist.
You could use Django signals instead to check before save.
I'm using django-rest-swagger to document and test an API and it has been working very well up to now but the following error has occured:
AttributeError at /docs/api-docs/app
'PeriodSerializer' object has no attribute 'get_fields'
'PeriodSerializer' inherits from serializers.BaseSerializer:
class PeriodSerializer(serializers.BaseSerializer):
def to_representation(self, instance):
return {
'lower': instance.lower,
'upper': instance.upper
}
def to_internal_value(self, data):
data = json.loads(data)
date_lower = self.date_from_str(data["lower"])
date_upper = self.date_from_str(data["upper"])
# some code omitted for brevity
return DateTimeTZRange(lower=date_lower, upper=date_upper)
#staticmethod
def date_from_str(datestr):
# code omitted for brevity
The code itself works fine, it's just that django-rest-swagger appears to have a problem with it. I'm using:
Python 3.4.0
Django 1.8.2
DRF 3.1.3
django-rest-swagger 0.3.2
Any help would be much appreciated.
Django Rest Framework's BaseSerializer doesn't have a get_fields function. You can see that in that in the source.
Short answer: Use Serializer, not BaseSerializer. Your code will work the same, and you won't have to worry about it. If for some reason you need to use BaseSerializer and django-rest-swagger together, you'll have to implement get_fields yourself.
If you look at the implementation of get_fields in a higher-level serializer (like Serializer) you'll see get_fields is defined like so:
def get_fields(self):
"""
Returns a dictionary of {field_name: field_instance}.
"""
# Every new serializer is created with a clone of the field instances.
# This allows users to dynamically modify the fields on a serializer
# instance without affecting every other serializer class.
return copy.deepcopy(self._declared_fields)
Using BaseSerializer, you won't have access to self._declared_fields either. You can see how that works in the linked source above, but the gist of it is that it returns a dictionary of attributes of the Field type.
Any instances of Field included as attributes on either the class
or on any of its superclasses will be include in the
_declared_fields dictionary.
I hope this helps answer your question!
Though late to the party but what works for me is something like this:
#six.add_metaclass(serializers.SerializerMetaclass)
class PeriodSerializer(serializers.BaseSerializer):
def get_fields(self):
"""
Returns a dictionary of {field_name: field_instance}.
"""
# Every new serializer is created with a clone of the field instances.
# This allows users to dynamically modify the fields on a serializer
# instance without affecting every other serializer class.
return copy.deepcopy(self._declared_fields)
i.e you need to decorate the class with a meta class which provides for _declared_field and then you can implement this method.
You could also try out drf-yasg. It is another swagger generator with support for the newer versions of Django Rest Framework.
I'm trying to implement a workflow process through the REST Api for an object to handle.
At the step 1, all fields could be written (except the status).
When calling a method (route on the viewset class), the status change (reflecting the change of the state of the object).
class ExaminationViewSet(viewsets.ModelViewSet):
model = Examination
#detail_route(methods=['GET'])
def invoice(self, request, pk=None):
current_examination = self.get_object()
current_examination.status = 1
current_examination.save()
return Response({'invoice':'waiting for paiment'})
Now it is not possible to edit some fields (but not all) on the object.
I thought that overriding get_serializer_class could be a solution, but the serializer class depends on the "state" of the instance. I'm not sure that it could be the way.
Do you have any idea to solve this problem ?