This is related to the question : Assymetric nature of GET and POST in a Django REST framework Serializer . I've put it as a fresh question, instead of putting more questions in that thread, accordingly to SO guidelines
I am writing a Viewset and a ModelSerializer for the User model to provide a /user endpoint
GET - returns list and information about all users, in the standard DRF way
POST - all I want the client to post is the facebook access_token (hence have put all other fields as read_only in serializer. The pre_save() in ViewSet is wired to use this access token and it uses django-facebook to pull data from facebook api (using access token) and automatically create a new user with that information. Since this new user is created automatically, I want to suppress the normal DRF flow during POST and not create another user via DRF. How do i do this?
views.py
from open_facebook import OpenFacebook
from django_facebook.api import FacebookUserConverter
from django_facebook.connect import connect_user
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = UserSerializer
def pre_save(self, obj):
access_token = obj.access_token
facebook = OpenFacebook(access_token)
conv = FacebookUserConverter(facebook)
action, user = connect_user(self.request, access_token)
# this creates an entire new row, just as required, in the variable "user", so all I want to do is suppress any other row creation in the standard POST method. connect_user fills in data like first_name, last_name, etc from facebook already, and that is exactly what I need to do.
conv.get_and_store_friends(user)
obj = user
user.delete()
# I am trying to do that by copying user to obj and deleting user, but at the end of it i
print obj.username
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
"""
User Serializer
"""
class Meta:
model = models.User
fields = ('id', 'username', 'first_name', 'last_name', 'activities', 'image_url', 'url', 'access_token')
read_only_fields = ('username', 'first_name', 'last_name', 'image_url', 'activities') #todo: find out a shortcut to invert selection
# show activities with user details rather than separately to remove an extra server call
depth = 1
using the create() function of ModelViewSet worked, instead of pre_save - to suppress saving the object
Related
I am trying to build an endpoint for the default User model, which we have in django, which being imported from from django.contrib.auth.models import User,
This is my serializer:-
class SuperUserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'id',
'username',
'groups',
'user_permissions',
'organization_users',
)
and the views goes as this:-
class UserDetailView(RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = SuperUserDetailSerializer
with the above codes, the number of queries which being hit are 216-219, which is due to the field user_permissions, I checked the User model, but it doesn't contains any field as user_permissions, but in the response, I get this field, and also in the html form field the permissions are populated(DRF Browsable page). I am trying to implement prefetch_related or select_related for the user_permissions field, but I am not able to reduce the the number of query, (It is hitting the database each time for getting all the permissions).
This is what I am trying to do but not able to achieve it:-
queryset = User.objects.prefetch_related(Prefetch('user_permissions', queryset = Permission.obects.select_related('content_type')))...I am not sure where I am doing the mistake.
I am fairly new to Django and can just about articulate what I am trying to do, so I apologise if the title isn't accurate.
I am using this survey app - https://github.com/Pierre-Sassoulas/django-survey
Within admin.py, I have the following two classes:
class SurveyAdmin(admin.ModelAdmin):
list_display = ('name', 'is_published', 'need_logged_user', 'template')
list_filter = ('is_published', 'need_logged_user')
inlines = [CategoryInline, QuestionInline]
actions = [make_published]
class ResponseAdmin(admin.ModelAdmin):
list_display = ('interview_uuid', 'survey', 'created', 'user')
list_filter = ('survey', 'created')
date_hierarchy = 'created'
inlines = [
AnswerTextInline, AnswerRadioInline, AnswerSelectInline,
AnswerSelectMultipleInline, AnswerIntegerInline
]
# specifies the order as well as which fields to act on
readonly_fields = (
'survey', 'created', 'updated', 'interview_uuid', 'user'
)
These create the pages for "Responses" and "Surveys" in the Admin page.
Admin area with Response and Survey links / their outputs
I can list the usernames of each user that submitted a Response to the Survey (using TabularInLine and model.response), but what I want to do is list all of the users that exist (so that I can add a timestamp of when they submitted their Response to the Survey/Filter by Response date/etc.).
What I've tried since is importing User from contrib.auth.models and creating a new UserAdmin class (so that I can at least list all users, and move on from there).
from django.contrib.auth.models import User
...
admin.site.unregister(User)
...
class UserAdmin(admin.ModelAdmin):
list_display = ('username',)
...
admin.site.register(User, UserAdmin)
I don't get any errors, but it's not displaying the UserAdmin class in the Admin page.
How can I list all of the registered users into the Admin page (in order to later add the timestamp of when a Response was submitted, if one was submitted)?
I'm using Django 1.11 if that's relevant.
I think you should have a list of all the user in the admin with the code you gave. Maybe you don't have any users in database ? I can't see the exact problem right now.
But without modifying the app, you should be able to see when a "Response" has been submitted or updated with the created or updated attribute of the Response class :
For example if you want to list the user and date of submission of a survey contained in the survey variable:
for response in survey.reponses:
print(response.user, "answered at", response.created)
If you want to loop on all users and see if the survey has been answered:
for user in User.objects.all():
response = Reponse.objects.filter(survey=survey, user=user)
if response:
print(user, "answered at", response.created)
else:
print(user, "never answered")
I hope this help.
I have this model:
class MyModel(User):
#others fields
and this serializer:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('username', 'password', 'some_field')
I get data from ajax to make a login and I handle it like this:
serializer = MySerializer(data=request.DATA)
print(serializer.is_valid())
The problem: When I send any data my serializer works but when my username field, which must be unique as User model describe, matches with one at the database the serialize become invalid, so serializer.is_valid() return False
Why? can not I create a serialize object with data which must be unique and already exists in the database?
Because you are using ModelSerializer which automatically generates validators for your serializer. You should use normal Serializer class instead.
Validation in REST framework
I am trying to make models and serializers that allow GET and POST
The GET will allow the client to see a list of all Users, and will show info like first name, last name, etc, but not access_token
However, the POST just needs the access_token and can pull all info like first name, last name, etc from Facebook.
How can I express and code this assymetric nature of get and post in the serializer
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
"""
User Serializer
"""
class Meta:
model = models.User
fields = ('id', 'username', 'first_name', 'last_name', 'image_url', 'activities', 'url', 'access_token')
views.py
class UserViewSet(viewsets.ModelViewSet):
"""
List all users - this should be taken out as it would never be used in app, and we wont want this as well, as app can only view friend details
Gives details of an user - this should stay
"""
queryset = models.User.objects.all()
serializer_class = UserSerializer
Any of these options:
Use read_only on fields that you don't want the user to be able to supply on creation.
Either override get_queryset and return a different serializer for GET vs POST.
Write the retrieve() and create() methods on the ViewSet explicitily.
However, the POST just needs the access_token and can pull all info like first name, last name, etc from Facebook.
You'll probably need a custom restore_object on the serializer to pull that info in.
I have 2 questions regarding proxy from model,
How to create a proxy object from model object?
How to create a proxy QuerySet from model QuerySet?
For example, assume we have defined:
from django.contrib.auth.models import User
class MyProxyUser(User):
class Meta:
proxy = True
def say_hello(self):
return "Hello, {}".format(self.get_full_name())
One way to convert successfully is by re-querying the object, which is unnecessary.
# Retrieve from model Object.
# Assume: request is HttpRequest from authenticated user.
my_user = MyProxyUser.objects.get(pk=request.user.pk) # Hit Database
my_user.say_hello()
# Retrieve from model QuerySet.
# Assume: users = User.objects.all()
MyProxyUser.objects.filter(pk__in=users)
You just need to assign the model attribute of your queryset
users = User.objects.all()
users.model = MyProxyUser
users.first().say_hello()
Edit: For assigning proxy class to a django model object, try
user = User.objects.all()
user.__class__ = MyProxyUser
Adding to Ramast's answer, you can also build a function on your proxy model class to consistently do this for you:
class MyProxyUser(User):
...
#staticmethod
def from_user(user: UserManager):
""" Get a PAUser classed instance from a User object.
Useful for Signals and other Django defaults that send base User objects.
"""
user.model = PAUser
return user
...