Serializing custom model fields in TokenObtainPairSerializer - django-rest-framework

Im using djangorestframework-simplejwt. I want to make TokenObtainPairView take in first_name, last_name, phone_number and email to authenticate instead of the default username and password, so that I can instead make a /register endpoint that will both register and login in a single request. (For reference this is the urlpattern typically used with TokenObtainPairView typically
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
I've been able to make this work with writing normal serializer fields like below
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
first_name = serializers.CharField()
last_name = serializers.CharField()
email = serializers.EmailField()
class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer
Is there a way to use ModelSerializer to do this instead? e.g something like this (the below doesn't work)
class MyTokenObtainPairSerializer(TokenObtainPairSerializer, ModelSerializer):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']

Related

How to pass data between views and forms using form_valid

I have a simple UserRegistration view:
class UserRegistration(generic.CreateView):
form_class = RegisterForm
template_name = 'registration/registration.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
user = form.save()
login(self.request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect(self.success_url)
Which logs the User in after they register. I wanted to send an email confirmation letting the User know they successfully signed up. So I added a method to my RegisterForm:
class RegisterForm(UserCreationForm):
email = forms.EmailField()
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2')
def registration_email(request):
send_mail(
'Welcome aboard!',
'Thanks for signing up...',
NOTIFICATION_EMAIL,
['example#email.com'],
fail_silently=True
)
Then I call it in my view with form.registration_email().
Now, this works but it's hardcoded. I want the recipient email to be the new User's email they just entered. That info should be available in the view, but how do I pass that to my form? I just can't figure out how to pass data from my views to my form methods, so that I can then call those methods in my view if form is valid.
Should I be doing it like this? Or should I be doing all this in form_valid instead? I also plan on using Celery in the future, so is there a preferred way with regards to that as well?
Where should to be send_email method depends on your logic in project.
In your case:
class RegisterForm(UserCreationForm):
...
def registration_email(self, request):
# it should be only after from.is_valid()
# to protect us:
if self.is_valid():
send_mail(
'Welcome aboard!',
'Thanks for signing up...',
NOTIFICATION_EMAIL,
[self.cleaned_data['email']],
fail_silently=True
)

django rest-framework add fields to ModelSerializer

I have the following serializer:
class AMXModXAdminsSerializer(mixins.GetCSConfigMixin, serializers.ModelSerializer):
admin = serializers.CharField(label='Admin', max_length=35, required=True, write_only=True)
password = serializers.CharField(label='Password', max_length=35, required=False, write_only=True)
access_flags = serializers.MultipleChoiceField(choices=ACCESS_FLAGS_OPTIONS, required=True, write_only=True)
account_flags = serializers.MultipleChoiceField(choices=ACCOUNT_FLAGS_OPTIONS, required=True, write_only=True)
class Meta:
model = CS16Server
fields = ('name', 'amxadmins', 'admin', 'password', 'access_flags', 'account_flags')
read_only_fields = ('name', 'amxadmins',)
When I try to access the url it complains:
Got AttributeError when attempting to get a value for field `admin` on serializer `AMXModXAdminsSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `CS16Server` instance.
Original exception text was: 'CS16Server' object has no attribute 'admin'.
If I add write_only to each field, the error will go away.
The thing is that I have a similar serializer, for the same model, with fields which do not belong to the model and it works perfectly without adding "write_only=True" to each field.
Any idea why one would work and another one no ?
What do u mean "when i access" ? post get put patch ?
Error says:
'CS16Server' object has no attribute 'admin'.
Does it ? if not , where do u intend to write it to ?
If model does not have admin field (as mentioned in error ) you need something like this:
class AMXModXAdminsSerializer(mixins.GetCSConfigMixin, serializers.ModelSerializer):
admin= serializers.SerializerMethodField()
fields ...
...
def get_admin(self, obj):
do somthing with self (contains the request) or the obj you're working on
return theOUTcome
If you set required=False it will not complain anymore because it will not try to get those fields values from db.

Multiple endpoints for a single model in REST framework

I have a REST framework app for a multi-page form:
class InformationRequest(models.Model):
# user information
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
# contact details
phone = models.CharField(max_length=60)
email = models.CharField(max_length=60)
I'm trying to create endpoints for each of the two blocks of data within the model:
UserInformationSerializer(serializers.Serializer):
first_name = serializers.CharField(max_length=60)
last_name = serializers.CharField(max_length=60)
ContactDetailsSerializer(serializers.Serializer):
phone = serializers.CharField(max_length=60)
email = serializers.CharField(max_length=60)
I'd like the endpoints to look like:
requests/1/user-informtion
requests/1/contact-details
But I'm unsure of how to structure the view to achieve this. Currently I'm using a model viewset:
class InformationRequestViewSet(viewsets.ModelViewSet):
queryset = InformationRequest.objects.all()
serializer_class = ??
Is it possible to have two serializers for one model?
It's certainly possible to have 2 (or any number of) serializers for a model. And you are on the right path. What you want is different urls mapping to different views. So in your case, it can be something like the following:
Note that I turned each of your serializers into a ModelSerializer.
path-to/serializers.py
class UserInformationSerializer(serializers.ModelSerializer):
class Meta:
model = InformationRequest
fields = ('first_name', 'last_name')
class ContactDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = InformationRequest
fields = ('phone', 'email')
Next, we have 2 different urls that point to 2 different views:
path-to/urls.py
urlpatterns = [
url(r'^requests/(?P<pk>\d+)/user-information/$', views.UserInformationDetail.as_view()),
url(r'^requests/(?P<pk>\d+)/contact-details/$', views.ContactInformationDetail.as_view()),
# ... other urls
]
And finally, the views themselves (I'm using generic RetrieveAPIView for convenience)
path-to/views.py
class UserInformationDetail(generics.RetrieveAPIView):
queryset = InformationRequest.objects.all()
serializer_class = UserInformationSerializer
class ContactInformationDetail(generics.RetrieveAPIView):
queryset = InformationRequest.objects.all()
serializer_class = ContactDetailsSerializer

Create a serializer for custom user model in django-rest-auth?

I've installed django-rest-framework. Also i've installed django allauth.
Now i want to use django-rest-auth based on previous two, for serializing data
and sending it in json format as answer on my submit form method (for registration/login etc).
I look up into documentation is provided for django-rest-auth
but it feels for me strange, as i inherit(and must do so) from the AbstractBaseUser and not simply do OneToOneField on existing User model.
At the moment the model i assign to the AUTH_USER_MODEL in my settings.py is:
class Account(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, db_index=True, verbose_name='Account Email')
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_employer = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
The question is: how can i use serializing in rest-auth the same way, but using my model instead of provided UserProfile model example ?
Yes you may inherit it, and use serializers the way you are saying. It should work. Please state where the error arises when you are using serializers for this.

Django charfield blank=False, but override required=False in admin

So I have some charfields that I want to always be required in forms, except for in the admin console. They are also 'list_editable'.
This means the easiest way to do it is to set blank=False on the model, but override required=False just for the admin form.
class MyModel(models.Model):
first_name = models.CharField(max_length=40, null=True)
class MyAdminForm(forms.ModelForm):
first_name = forms.CharField(max_length=40, required=False)
class MyAdmin(admin.ModelAdmin):
list_editable = ('first_name', ...)
form = MyAdminForm
However when a value is set, and you want to set it to null, you can't.
Django won't save it as null in the DB, it will rather try to save it as the empty string "" since it is a charfield.

Resources