Better way to save data in database using django serializer - python

Hello I am working on a project in which I'm making serializers
and I send post requests using postman. This is working fine but this is a very small part of the application and the code will grow to be very large. If there is a better way of doing this in which I write smaller code then I would like to employ that.
Right now the way I'm saving the information is as follows
models.py
class Services(models.Model):
business = models.ForeignKey(Business, on_delete=models.CASCADE)
service = models.CharField(max_length=30, blank=True)
serializers.py
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Services
fields = ('id', 'business', 'service')
views.py
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = BusinessSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
urlpatterns = [
path('service/', ServiceAPIView.as_view()),
]
Edited*
Here is the business model
class Business(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
business_name = models.CharField(max_length=50, blank=True)
invoices_terms = models.TextField(max_length=100, blank=True)
desc = models.TextField(max_length=1000, blank=True)
website = models.URLField(max_length=200, blank=True)
tax_percentage = models.DecimalField(blank=True, max_digits=5, decimal_places=3)
labour_rate_per_hour = models.DecimalField(blank=True, max_digits=5, decimal_places=2)
tax_hst_number = models.IntegerField(blank=True)

Well the code looks ok and it's also ok to have deep modules.
A little update could be the following:
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated,]
serializer_class = ServiceSerializer
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
In this way you can write only one Response line for each view request method and avoid to use if/else statements

Related

Django Post method not showing form to fill fields

The post method does not bring a form to enter the field details. I am able to enter data using json format. I followed the Django documentation to create the API
This is my serializer code:
from rest_framework import serializers
from .models import Tasks
# converting objects into datatypes understandable by javaScript and frontend frameworks
class TasksSerializers(serializers.ModelSerializer):
class Meta:
model = Tasks
fields = "__all__"
views.py
#api_view(['GET', 'POST'])
def task_list(request, format=None):
serializer_class = TasksSerializers
if request.method == 'GET':
tasks = Tasks.objects.all()
serializer = TasksSerializers(tasks, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = TasksSerializers(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)
print(request.method)
return Response(serializer.data, status=status.HTTP_201_CREATED)
models.py
class Tasks(models.Model):
task_id = models.AutoField(db_column="task_id", primary_key=True, unique="true")
task_description = models.TextField(db_column="task_description", null=True)
task_status= models.CharField(max_length=399, db_column="task_status", default="Not started" )
task_priority = models.CharField(max_length=399, db_column="task_priority", default="Low priority")
asignee_name = models.TextField(db_column="asignee_name", blank=False, null=False)
deadline = models.DateField(db_column="deadline", blank=False, null=False)
emp = models.ForeignKey(Employee,on_delete=models.CASCADE,default='')
# metadata for the table
class Meta:
db_table = "Tasks"
What have I done wrong?

How to send several fields in the response with a PUT request?

I would like when my PUT request is successful it returns me a response with all the fields in my PlantSerializer because currently the response returns me this:
{
"id":48,
"name":"Jar",
"width":"50",
"height":"50",
"exposure":"None",
"qr_code":"",
"garden":65,
"plant":[
7
]
}
But, I would like the response to return this instead:
{
"id":48,
"name":"Jar",
"width":"50",
"height":"50",
"exposure":"None",
"qr_code":"",
"garden":65,
"plant":[
"id":7,
"name":"Artichoke",
"image":null
]
}
How can I achieve this result?
Here is my serializers and my model class :
class Plot(models.Model):
name = models.CharField(max_length=50)
garden = models.ForeignKey('perma_gardens.Garden', on_delete=models.CASCADE)
width = models.CharField(max_length=50, blank=True, null=True)
height = models.CharField(max_length=50, blank=True, null=True)
plant = models.ManyToManyField('perma_plants.Plant', related_name='%(class)s_plant', blank=True)
# Here is my serializers :
class GardenSerializer(serializers.ModelSerializer):
class Meta:
model = Garden
fields = ('id', 'name',)
class PlantSerializer(serializers.ModelSerializer):
class Meta:
model = Plant
fields = ('id', 'name', 'image')
class ReadPlotSerializer(serializers.ModelSerializer):
garden = GardenSerializer(required=True)
plant = PlantSerializer(many=True)
id = serializers.IntegerField(read_only=True)
class Meta:
model = Plot
fields = '__all__'
read_only_fields = [fields]
class WritePlotSerializer(serializers.ModelSerializer):
class Meta:
model = Plot
fields = '__all__'
And here is my views :
class PlotViewSet(viewsets.ModelViewSet):
queryset = Plot.objects.all()
def create(self, request, *args, **kwargs):
serializer = WritePlotSerializer(data=request.data, many=isinstance(request.data, list))
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
def partial_update(self, request, *args, **kwargs):
instance = self.queryset.get(pk=kwargs.get('pk'))
serializer = WritePlotSerializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def get_serializer_class(self):
if self.action in ("list", "retrieve"):
return ReadPlotSerializer
return WritePlotSerializer
In the fuction partial_update you are utilizing the WritePlotSerializer, which only has the plant field implicitly through the fields=__all__ value. This is probably causing drf to use a PrimaryKeyRelatedField, and as such you don't get all the extra fields you defined in your PlantSerializer.
If I understand correctyl you want to use the WritePlotSerializer in the update but use the ReadPlotSerializer when returning the object. You probably should combine both of them in a single serializer by overriding the update method in order to support updating the nested Plant objects. Here is the related documentation.
Alternatively, if you don't want to update the Plants values you can use a slightly modified version of the ReadPlotSerializer in all calls:
class PlotSerializer(serializers.ModelSerializer):
garden = GardenSerializer(required=True, read_only=True)
plant = PlantSerializer(many=True, read_only=True)
id = serializers.IntegerField(read_only=True)
class Meta:
model = Plot
fields = '__all__'

creating post on a review model

I need help leaving a review to a another model as a user, ive been having errrors and tried a lot of solutions.
error
FieldError at /reviews/1
Cannot resolve keyword 'user' into field. Choices are: id, pet_owner, pet_owner_id, rating, review, sitter, sitter_id
This is my view function:
def post(self, request, pk):
user = Review(pet_owner = request.user)
sitter = get_object_or_404(Sitter, pk=pk)
data = request.data
review = Review.objects.create(
pet_owner_id = user,
sitter= sitter,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
and these are the models:
class Review(models.Model):
review = models.CharField(max_length=500)
rating = models.DecimalField(max_digits=7, decimal_places=2)
sitter = models.ForeignKey(Sitter,on_delete=models.CASCADE,null=True)
pet_owner = models.ForeignKey(get_user_model(),on_delete=models.CASCADE,null=True)
class Sitter(models.Model):
first_name = models.CharField(max_length=255, default='Jane')
last_name = models.CharField(max_length=255, default='Doe')
zipcode = models.CharField(max_length = 5, default='12345')
supersitter = models.BooleanField(null=True, blank=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
numReviews = models.IntegerField(null=True, blank=True, default=0)
rating = models.DecimalField(max_digits=7, decimal_places=2, default=0)
this is my review model and sitter model
def post(self, request):
"""Create request"""
print(request.data)
# Add user to request data object
# Serialize/create review
review_user = request.user
review_data = Review(pet_owner = review_user)
review = ReviewSerializer(review_data, data=request.data)
# If the review data is valid according to our serializer...
if review.is_valid():
# Save the created review & send a response
r = review.save()
return Response({ 'review': review.data }, status=status.HTTP_201_CREATED)
# # If the data is not valid, return a response with the errors
return Response(review.data, status=status.HTTP_400_BAD_REQUEST)
we also tried this
user should be a simple User object, so:
def post(self, request, pk):
sitter = get_object_or_404(Sitter, pk=pk)
data = request.data
review = Review.objects.create(
pet_owner=request.user,
sitter=sitter,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
You can slightly optimize the method by using the pk immediately:
def post(self, request, pk):
review = Review.objects.create(
pet_owner=request.user,
sitter_id=pk,
rating=data['rating'],
review=data['review']
)
return Response('Review Added', status=status.HTTP_400_BAD_REQUEST)
but it might be better to work with a serializer to properly validate and clean the request data.

Save reversal relationship item in database using one POST - Django

I want a viewset that handles a post request that creates some nested objects using the post data.
I have these models, serializers, and views:
Models:
class Connection(models.Model):
from portfolio.models import Portfolio
user = models.ForeignKey(User, related_name='exchange_connections', on_delete=models.CASCADE)
portfolios = models.ManyToManyField(Portfolio)
class ConnectionSettings(models.Model):
exchange_connection = models.OneToOneField(Connection, to_field='id', primary_key=True,
related_name='settings', on_delete=models.CASCADE)
import_past_transactions = models.BooleanField(default=False, blank=True)
class ConnectionCredentials(models.Model):
exchange_connection = models.OneToOneField(Connection, to_field='id', primary_key=True,
related_name='credentials', on_delete=models.CASCADE)
key = models.CharField(max_length=300, blank=False, null=False)
secret = models.CharField(max_length=300, blank=False, null=False)
passphrase = models.CharField(max_length=300, blank=True, null=True)
Serializers:
class ConnectionCredentialsSerializer(FlexFieldsModelSerializer):
class Meta:
model = ConnectionCredentials
fields = '__all__'
class ConnectionSettingsSerializer(FlexFieldsModelSerializer):
class Meta:
model = ConnectionSettings
fields = '__all__'
class ConnectionSerializer(serializers.ModelSerializer):
credentials = ConnectionCredentialsSerializer()
settings = ConnectionSettingsSerializer()
class Meta:
model = Connection
fields = '__all__'
def create(self, validated_data):
credentials = validated_data.pop('credentials')
settings = validated_data.pop('settings')
connection = Connection.objects.create(**validated_data)
ConnectionCredentials.objects.create(exchange_connection=connection, **credentials)
ConnectionSettings.objects.create(exchange_connection=connection, **settings)
return connection
Views:
class ConnectionViewSet(viewsets.ViewSet):
serializer_class = serializers.ConnectionSerializer
queryset = exchange_models.Connection.objects.all()
permission_classes = (IsAuthenticated, core_permissions.IsMineOnly)
def list(self):
return HttpResponse(self.request.user.exchange_connections_set)
def retrieve(self, request, pk=None):
serialized_data = self.serializer_class(exchange_models.Connection.objects.get(id=pk)).data
return HttpResponse(serialized_data)
def create(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.create(serializer.data)
serializer.save()
return Response({'status': 'connection created.'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
URLs:
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'connections', views.ConnectionViewSet)
urlpatterns = [
path('', include(router.urls)),
]
POST Request:
When I send the post request, the portfolios and the user already exist. So I should only give primary keys to these rows in my request.
But I need to create new rows for Settings and Credentials for which I should pass data in the request.
By default nested serializers are read-only. You will have to customize the create method to make it writable.
class ConnectionSerializer(serializers.ModelSerializer):
portfolios = PortfolioSerializer(many=True)
credentials = ConnectionCredentialsSerializer(many=False)
settings = ConnectionSettings(many=False)
class Meta:
model = models.Connection
exclude = ('user',)
read_only_fields = ('date_created', 'last_updated')
def create(self, validated_data):
portfolios = validated_data.pop('portfolios')
credentials = validated_data.pop('credentials')
settings = validated_data.pop('settings')
connection = Connection.objects.create(**validated_data)
for portfolio in portfolios:
Portfolio.objects.create(exchange_connection=connection, **portfolio)
for credential in credentials:
ConnectionCredentials.objects.create(exchange_connection=connection, **credentials)
for setting in settings:
ConnectionSettings.objects.create(exchange_connection=connection, **settings)
return connection
Provided that following are the model names relative to serializers.
PortfolioSerializer -> Portfolio,
ConnectionCredentialsSerializer -> ConnectionCredentials

"This field is required" POSTing to Django rest framework

I am trying to POST to my django rest framework serializer using httpie and the following syntax:
http POST http://127.0.0.1:8000/traffic/geofences/
{
"id":13,
"radius":40,
"lat":0,
"long":0,
"name":"B"
}
The following error is returned:
{
"id":["This field is required."],
"name":["This field is required."],
"lat":["This field is required."],
"long":["This field is required."],
"radius":["This field is required."]
}
I don't understand why these fields are not accepted. What should the format look like? The server will be sent data from a mobile device and inserting it into the database via django rest framework.
Here is my Serializer and Model and View
Serializer:
class GeofenceSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(min_value=0)
name = serializers.CharField()
lat = serializers.DecimalField(max_digits=10, decimal_places=6, coerce_to_string=False)
long = serializers.DecimalField(max_digits=10, decimal_places=6, coerce_to_string=False)
radius = serializers.IntegerField(min_value=0)
class Meta:
model = Geofence
fields = ('id', 'name','lat', 'long', 'radius')
def create(self, valid_data):
return Geofence.objects.create(**valid_data)
Model:
class Geofence(models.Model):
id = models.IntegerField(default=0, primary_key=True)
name = models.CharField(max_length=200, default="Geofence", blank=False)
lat = models.DecimalField(default=0, decimal_places=6, max_digits=10, blank=False)
long = models.DecimalField(default=0, decimal_places=6, max_digits=10, blank=False)
radius = models.IntegerField(default=10, blank=False)
def __str__(self):
return "Geofence: " + str(self.name);
View:
class GeofencesView(APIView):
model = Geofence
serializer_class = GeofenceSerializer
def getId(self):
return uuid.uuid4().int & (1<<64)-1
def get(self, request):
fences = Geofence.objects.all()
serializer = GeofenceSerializer(fences, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = GeofenceSerializer(data= request.data)
serializer.id = -1
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors)
I have read the django rest framework documentation and the django docs. The get request works fine. If nothing else, I would appreciate someone pointing me to where this is discussed in either documentation.
I don't think you're using the correct json format for httpie.
Try this instead:
http POST http://127.0.0.1:8000/traffic/geofences/ \
id:=13 radius:=40 lat:=0 long:=0 name="B"

Categories

Resources