How to include custom functions in django serializers? - python

I've been trying all sorts of method but still cant seem to get this task done even through asking here with my previous questions. The problem is, I am trying to convert my data that is entered by the user on django's admin page to json data using rest. But before converting the data to json, I have 2 custom functions that validates the email and phone number fields entered by the user that uses packages from PyPI and I need these functions to run through the entered text values on the admin page. I dont know where to put my 2 functions in my python files, whether in models.py or serializers.py, Ive actually tried both ways but cant seem to get it to work.
/* models.py */
import re
import phonenumbers
from django.db import models
from phonenumbers import carrier
from validate_email import validate_email
class razer(models.Model):
emailplus = models.EmailField()
country = models.CharField(max_length=2)
phone_number = models.CharField(max_length=100)
def clean_emailplus(self):
email = self.cleaned_data.get("emailplus")
if not validate_email(email, check_mx=True, verify=True):
raise models.ValidationError("Invalid email")
return email
def clean_phone_number(self):
phone_number = self.cleaned_data.get("phone_number")
clean_number = re.sub("[^0-9&^+]", "", phone_number)
# alpha_2 = self.cleaned_data.get("country")
alpha_2 = self.cleaned_data.get("country")
z = phonenumbers.parse(clean_number, "%s" % (alpha_2))
if len(clean_number) > 15 or len(clean_number) < 3:
raise forms.ValidationError(
"Number cannot be more than 15 or less than 3")
if not phonenumbers.is_valid_number(z):
raise forms.ValidationError(
"Number not correct format or non-existent")
if carrier.name_for_number(z, "en") == '':
raise forms.ValidationError("Please enter a mobile number")
return phonenumbers.format_number(
z, phonenumbers.PhoneNumberFormat.E164)
def __str__(self):
return self.emailplus
/* serializers.py */
import re
import phonenumbers
from rest_framework import serializers
from .models import razer
from phonenumbers import carrier
from validate_email import validate_email
class razerSerializer(serializers.ModelSerializer):
class Meta:
model = razer
fields = ('emailplus', 'country', 'phone_number')
/* views.py */
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import razer
from .serializers import razerSerializer
class razerList(APIView):
def get(self, request):
razer1 = razer.objects.all()
serializer = razerSerializer(razer1, many=True)
return Response(serializer.data)
def post(self):
pass
Also if I put the phonenumber and email fields in my serializers.py as in phone_number = serializers.CharField(), and then put the 2 custom functions as well, do I have to register serializer as a model and what should my views.py be changed into as it is using models.py currently . Please tell me what changes should be made, as I need to validate the text values entered by the users on the admin page on the registered model or via another method where users can enter the text separately.

You could create a custom validator on your serialize for emailplus field.
def clean_emailplus(value):
email = value
if not validate_email(email, check_mx=True, verify=True):
raise serializers.ValidationError("Invalid email")
class razerSerializer(serializers.ModelSerializer):
emailplus = serializers.StringField(validators=[clean_emailplus])
class Meta:
model = razer
fields = ('emailplus', 'country', 'phone_number')

If you are creating a Django rest framework application, you need to use the validate method in the serializer.
def validate(self, data):
# clean the data
return data

Related

DRF + Djongo: How to access and work with ID from legacy database?

I set up a Django REST Framework (DRF) in combination with djongo, since I'm having a legacy database in MongoDB/Mongo express. Everything runs through a docker container, but I don't think that this is the issue. My model looks like this:
from django.db import models
## Event
class Event(models.Model):
ID = models.AutoField(primary_key=True)
date = models.DateField(default=None)
name = models.CharField(default=None, max_length=500)
locality = models.CharField(default=None, max_length=500)
class Meta:
ordering = ['ID']
db_table = 'event'
Next, my serializer is as follows:
from rest_framework import serializers
from .models import Event
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields= "__all__"
And the view looks like this:
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import *
from .models import *
## Events
#api_view(['GET', 'POST'])
def event_list(request):
if request.method == 'GET':
events = Event.objects.all().order_by('ID')
event_serializer = EventSerializer(events, many=True)
return Response(event_serializer.data)
elif request.method == 'POST':
event_serializer = EventSerializer(data=request.data)
if event_serializer.is_valid():
event_serializer.save()
return Response(event_serializer.data, status=status.HTTP_201_CREATED)
return Response(event_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
As soon as I'm trying to post with
import requests
endpoint = "http://localhost:8080/event/"
get_response = requests.post(endpoint, {"date":"2022-06-01", "name":"Max Musterfrau", "locality":"North America"} )
I'm getting back the error message
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'ObjectId'
Following the documentation and other StackOverflow questions, in my model ID = models.AutoField(primary_key=True) should create the index ID as the primary key and auto-increments it. But at the same time, MongoDB creates its own _id and the actual ID from my legacy database doesn't auto-increment. I've tried both suggested solutions from here, but unfortunately it didn't change a thing. Therefore, my question is: How can I auto-increment the ID from my legacy database when POSTing an object? How do I create it automatically?
We need to create Separate serializer filed for _id = ObjectIdField like below
from bson import ObjectID
from bson.errors import InvalidID
class ObjectIdField(serializers.Field):
""" Serializer field for Djongo ObjectID fields """
def to_internal_value(self, data):
# Serialized value -> Database value
try:
return ObjectId(str(data)) # Get the ID, then build an ObjectID instance using it
except InvalidId:
raise serializers.ValidationError(
'`{}` is not a valid ObjectID'.format(data)
def to_representation(self, value):
# Database value -> Serialized value
if not ObjectId.is_valid(value): # User submitted ID's might not be properly structured
raise InvalidId
return smart_text(value)
class EventSerializer(ModelSerializer):
_id = ObjectIdField(read_only=True)
class Meta:
model = Event
fields = '__all__'

Problem sending information using POST method in django rest framework

Hi i'm knew in django rest framework:
views.py:
from django.shortcuts import render
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookModelSerializers
# for posting data
class PostModelData(APIView):
def post(self, request):
serializer = BookModelSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py:
from django.db import models
from datetime import datetime
class Book(models.Model):
name = models.CharField(max_length=150, blank=True)
store_name = models.CharField(max_length=50, blank=True)
description = models.TextField(blank=True)
image = models.ImageField(
default='', upload_to='store_image/', null=True, blank=True)
fav = models.BooleanField(default=False, blank=True)
created_at = models.DateTimeField(default=datetime.now())
def __str__(self):
return self.name
urls.py:
from django.urls import path
from .views import GetAllData, Getfavdata, UpdateFavData, PostModelData
urlpatterns = [
path('getalldata/', GetAllData.as_view()),
path('getfavdata/', Getfavdata.as_view()),
path('updatefavdata/<int:pk>/', UpdateFavData.as_view()),
path('postmodeldata/', PostModelData.as_view()),
]
serializers.py:
from rest_framework import serializers
from .models import Book
class BookModelSerializers(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
When I post ('name','store_name','description','fav') with postman, the data that is stored is without details.
I wanted to know what the problem is?
I also removed (blank = True) in models.py, nothing is saved.
enter image description here
Ah, I looked at the picture your attached and found what's wrong.
You're sending your data as GET parameters in the URL. E.g. https://localhost:8000?name=lorem&storename=lorem2.
However, you have defined your view method to expect a POST request:
class PostModelData(APIView):
#Use this method for POST requests
def post(self, request):
pass
#Use this method for GET requests
def get(self, request):
pass
In this case though, when you want to receive data from the user and save it POST request is the correct choice, you just put your data in the wrong place.
Instead do this:
1: Change your code in your view
class PostModelData(APIView):
def post(self, request):
#If you want to send MULTIPLE entries per request
serializer = BookModelSerializers(data=request.data, many=True)
#If you want to send ONE entry per request
serializer = BookModelSerializers(data=request.data, many=False)
2: Go to Postman and do the following steps.
3: Remove all parameters from url so you just have http://localhost:8000/api/v1/postmodeldata/
4: Right next to the URL, make sure you have POST selected.
5: Under the URL bar, click on Body.
6: In the radiobuttons change to raw. And in the dropdown menu right next to it select JSON.
7: In the textarea write something like in this picture:
Example request
8: Click Send and you should get a 201 response from the server.
class BookModelSerializers(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
def create(self, validated_data):
book = Book.objects.create(**validated_data)
book.save()
return book

how save user from custom form in database

Below is my code comprising of django forms.py and views.py file.
I want save new user. How to achieve this?
forms.py
from django import forms
class SignUpForm(forms.Form):
first_name = forms.CharField(widget=forms.TextInput(attrs={'class':'first'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'class':'first'}))
email = forms.EmailField(widget=forms.EmailInput(attrs={'class': 'first'}))
password = forms.CharField(widget = forms.PasswordInput(attrs={'class':'first'}))
re_password = forms.CharField(widget = forms.PasswordInput(attrs={'class':'first'}))
views.py
from django.shortcuts import render
from django.http import HttpResponse
import datetime
from . import forms
def regform(request):
form=forms.SignUpForm()
if request.method=='POST':
form=form.SignUpForm(request.POST)
else:
html="welcome"
return render(request,'home/home.html', {'html':html, 'form':form})
Have a look at thiswill be resourceful. Also, read Django Official docs on User management

How to do validation in django serializers?

I have 2 custom validation functions created using packages from PyPI, I want to inlcude them in my serializers.py in my django project before converting it to JSON using rest. Problem is i dont know where or how to put the functions in such that the code will run through it. Here is my code:
enter image description here(this is how im adding users right now, using the model's fields ive registered)
Here is my code:
/* serializers.py */
import re
import phonenumbers
from rest_framework import serializers
from phonenumbers import carrier
from validate_email import validate_email
class basicSerializer(serializers.Serializer):
emailplus = serializers.EmailField()
country = serializers.CharField(max_length=2)
phone_number = serializers.CharField(max_length=100)
def validate_emailplus(self):
email = self.validated_data.get("email")
if not validate_email(email, check_mx=True, verify=True):
raise serializers.ValidationError("Invalid email")
return email
/* views.py */
from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import basic
from .serializers import basicSerializer
class basicList(APIView):
def get(self, request):
basic1 = basic.objects.all()
serializer = basicSerializer(basic1, many=True)
return Response(serializer.data)
def post(self):
pass
As you can see I am not using models.py anymore and serializers.py as some sort of model with the given email and phone fields. In order for my function to work (which is not at the moment), it needs to use get() method from the entered data and do validate_email to know if the email exists. Please tell me what changes should be made, i do not know if the problem is with views.py which still uses models.py or if i should register the serializer as a model?
To run your validations, you must call serializer.is_valid(), however, that requires data to be passed to the serializer not instances. The logic behind this is that drf validates data coming in not data already stored in your DB
Correct logic
Considerations
It looks like you are implementing a list view, but you are validating the email address, which is probably not what you intended to do in the first place. I am guess you want to validate email on create.
You can make use of drf's generic views and mixins such as GenericViewSet, ListModelMixin, and ListModelMixin
I think you have a type in validate_emailplus where you try to get the field email while the serializer declares it as emailplus
You seem not to be following PEP-8 (style guide for Python)
serializers.py
import re
import phonenumbers
from rest_framework import serializers
from phonenumbers import carrier
from validate_email import validate_email
class BasicSerializer(serializers.Serializer):
emailplus = serializers.EmailField()
country = serializers.CharField(max_length=2)
phone_number = serializers.CharField(max_length=100)
def validate_emailplus(self):
email = self.validated_data.get("emailplus")
if not validate_email(email, check_mx=True, verify=True):
raise serializers.ValidationError("Invalid email")
return email
views.py
from rest_framework import mixins, viewsets
class BasicViewSet(
viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin,
):
queryset = Basic.objects.all()
serializer_class = BasicSerializer
For a better understanding of how viewset and mixins work, I recommend checking their implementation
Validation in Admin site
From the screenshot you added, it looks like you are trying to validate in the admin site, for that consider the below code:
models.py
class Basic(models.Model):
...
def clean(self):
if not validate_email(self.email, check_mx=True, verify=True):
raise ValidationError("Invalid email")
This works because Django admin generates forms based on your models and then the forms call full_clean() on the model, which calls clean()

Django form not selecting options

I have a django project, in the project i have a django forms.py which contain a field call category which select box input element that is generated as a result of a query on the database.
If I choose an option from the select dropdown from the database I keep getting the error:
**strong text**Select a valid choice. That choice is not one of the available choices
Below is the code:
Forms.py
from django import forms
#from django.contrib.auth.models import User
from signer.models import CreateSingleSigner
class CreateSingleSignerForm(forms.ModelForm):
category = forms.ModelChoiceField(
required = True,
help_text = 'category',
queryset=CreateSingleSigner.objects.all().values_list(
'category', flat=True
).distinct()
)
my views.
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from signer.models import CreateSingleSigner
from signer.forms import CreateSingleSignerForm
from django.template import RequestContext
def singlesigner(request):
context = {}
if request.method == 'POST':
createsinglesigner_form = CreateSingleSignerForm(data=request.POST)
if createsinglesigner_form.is_valid():
createsinglesigner.category = request.POST['category']
createsinglesigner_form.save()
else:
print createsinglesigner_form.errors
else:
# context['createsinglesigner'] = CreateSingleSigner()
createsinglesigner_form =CreateSingleSignerForm()
return render(request, "signer/singlesigner.html", {"createsinglesigner_form":createsinglesigner_form}, RequestContext(request))
my models.py
from django.db import models
class CreateSingleSigner(models.Model):
category = models.CharField(max_length = 32)
Can someone tell me where I am going wrong?
Try one of ways below to fix problem:
Try to define conversion of unicode in your CreateSingleSigner model:
class CreateSingleSignerForm(forms.Form):
def __unicode__(self):
return self. category
Explanations:
ModelChoiceField will use __unicode__ representation of specified fields for displaying and validating your fields.
Try to set choices in __init__ method of your form
class CreateSingleSignerForm(forms.Form):
category = forms.ChoiceField(choices=[], required=False)
def __init__(self, *args, **kwargs):
super(CreateSingleSignerForm, self).__init__(*args, **kwargs)
self.fields['category'].choices = CreateSingleSigner.objects.all().values_list('category', flat=True).distinct()
Explanations: The queryset parameter for ModelChoiceField cannot be values_list, because it's going to save the relationships, so django have to use complete model objects, not certain values of model objects.

Categories