Update django model database with ForeignKey by using serializer - python-3.x

I have created a django model which includes a foreign key to a user as follows:
from authentication.models import User
from django.db import models
class Event(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
dr_notice_period = models.IntegerField(blank=True, null=True)
dr_duration = models.IntegerField(blank=True, null=True)
dr_request = models.FloatField(blank=True, null=True)
My serializers.py file is as follows:
class EventSerializer(serializers.ModelSerializer):
user = UserSerializer(many=True, read_only=True)
class Meta:
model = Event
fields = ['user', 'dr_notice_period', 'dr_duration', 'dr_request']
What I need to do is to go to a url and with a POST request to upload the data to the database, but without specifically specifying the user.
My views.py is as follows:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from vpp_optimization.serializers import EventSerializer
#api_view(['POST'])
def event(request):
serializer = EventSerializer(data=request.data)
if serializer.is_valid():
instance = serializer.save(commit=False)
instance.user = request.user
instance.save()
return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
else:
return Response({"status": "error", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
As I study I thought that by using commit=False in save would solve the problem, but I am getting the following error:
'commit' is not a valid keyword argument to the 'save()' method. If you need to access data before committing to the database then inspect 'serializer.validated_data' instead. You can also pass additional keyword arguments to 'save()' if you need to set extra attributes on the saved model instance. For example: 'serializer.save(owner=request.user)'.'
Is there a better way to do what I intent to do?

You pass the user as parameter, so:
if serializer.is_valid():
instance = serializer.save(user=request.user)
return Response({'status': 'success', 'data': serializer.data}, status=status.HTTP_200_OK)
Note: It is normally better to make use of the settings.AUTH_USER_MODELĀ [Django-doc] to refer to the user model, than to use the User modelĀ [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Related

Django model relations access

Are there any easy way of accessing all the article comments when I have setup my models like this? If this is not the correct way of doing it, please let me know a better way to create a relationship.
My model,
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Article(models.Model):
author = models.ForeignKey(User,on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField()
def __str__(self):
return self.title
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=True)
comment = models.CharField(max_length=450, blank=False, null=True)
def __str__(self):
return f"{self.comment}"
Are there any easy query to access the comment when looping the article? Or is this the wrong way of setting up relations?

Field 'id' expected a number but got <User: ben>

I created a population script for a django website however, after running the script and making migrations, I logged in to the django admin page for my site to access the objects created using the script and could not do it for my Business model. It works fine for my other models.
I get this error when I try to access the registered Businesses in my database from the Django admin page. It does not seem to trace back to any of my code but rather to a template in the admin folder of my python environment. Here's the error message:
error message
Here are my models:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_pic = models.ImageField(upload_to="profile_images", blank=True, default="profile_images/default.png")
description = models.TextField(max_length=1024, default="")
# boolean flag for identifying business owners
is_business_owner = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Business(models.Model):
owner_fk = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=128)
address = models.CharField(max_length=128)
img = models.ImageField(upload_to="business_images", blank=True)
slug = models.SlugField(unique=True)
class Meta:
verbose_name_plural = 'Businesses'
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Business, self).save(*args, **kwargs)
def __str__(self):
return f"{self.name} owned by {UserProfile.objects.get(pk=self.owner_fk).username}"
Here is how I created the objects through the population script:
def add_user(username, firstname, lastname, password, profile_pic, description,
is_business_owner):
new_user = User.objects.get_or_create(username=username, password=password,
first_name=firstname, last_name=lastname)
if new_user[1]:
new_profile = UserProfile(user=User.objects.get(username=username))
new_profile.profile_pic = profile_pic
new_profile.description = description
new_profile.is_business_owner = is_business_owner
new_profile.save()
def add_business(owner, name, address, img):
new_business = Business(name=name, owner_fk=User.objects.get(username=owner))
new_business.address = address
new_business.img = img
new_business.save()
Any help would be appreciated, thanks.
Your Business class has got __str__ method. You are trying to pass User object to pk (id) key.
class Business(models.Model):
def __str__(self):
return f"{self.name} owned by {UserProfile.objects.get(pk=self.owner_fk).username}"
Instead of searching User object in database Change it to:
return f"{self.name} owned by {self.owner_fk.username}"
because you don't have to search database for related object, just address it directly.

NOT NULL constraint failed: jobs_job.created_by_id IntegrityError at /jobs/add/

I am trying to enable the user to be able to add job and after filling the form i got that error. Here is my code:
models.py
from django.db import models
from django.contrib.auth.models import User
class Job(models.Model):
title = models.CharField(max_length=255)
short_description = models.TextField()
long_description = models.TextField(blank=True, null=True)
created_by = models.ForeignKey(User, related_name='jobs', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
changed_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import AddJobForm
from .models import Job
def job_detail(request, job_id):
job = Job.objects.get(pk=job_id)
return render(request, 'jobs/job_detail.html', {'job': job})
#login_required
def add_job(request):
if request.method == 'POST':
form = AddJobForm(request.POST)
if form.is_valid():
job = form.save(commit=True)
job.created_by = request.user
job.save()
return redirect('dashboard')
else:
form = AddJobForm()
return render(request, 'jobs/add_job.html', {'form': form})
forms.py
from django import forms
from .models import Job
class AddJobForm(forms.ModelForm):
class Meta:
model = Job
fields = ['title','short_description','long_description']
What do i need to do to solve this error? I have try to remove the commit=True, from views.py and I have try to remove sqlite3 and do the migrations again.
You are trying to save the object without a value for created_by (hence the error message), then assign a value for that field and save again. You should change
job = form.save(commit=True)
to
job = form.save(commit=False)
so that the first .save doesn't actually try to commit anything to the database. It is simply used to construct the Job instance so that you can complete any missing values and actually save it using job.save().
Note that if you omit the commit= argument it is assumed to be True.

How to join two tables in django and serialize the same using one serializer?

I have been learning django and django rest framework since couple of weeks and I want to figure out how can I join two tables and serialize the data of same to return the json response using django rest framework.
I want to return result as json response:
{ 'user_id_id': 1, 'request_msg': 'Hi', 'response_msg': "Hi, Welcome" }
where result is
from django.db import connection
cursor = connection.cursor()
con = cursor.execute("SELECT backend_request_messages.user_id_id, backend_request_messages.request_msg as request_msg,backend_response_messages.response_msg as response_msg FROM backend_request_messages,backend_response_messages Where backend_request_messages.user_id_id=backend_response_messages.user_id_id=1 ")
Here is what I have tried :
#backend/Models.py
class User(models.Model):
username = models.CharField(max_length=50)
name = models.CharField(max_length=50, blank=True, null=True)
uid = models.CharField(max_length=12, blank=True, null=True)
age = models.CharField(max_length=3, blank=True, null=True)
active = models.BooleanField(default=True)
class Meta:
default_related_name = 'users'
def __str__(self):
return self.name
class Request_Messages(models.Model):
request_msg = models.CharField(max_length=100)
request_msg_created_at = models.DateTimeField(auto_now_add=True)
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, null=True)
class Meta:
default_related_name = 'request_messages'
def __str__(self):
return self.request_msg
class Response_Messages(models.Model):
response_msg = response_msg = models.CharField(max_length=400)
response_msg_created_at = models.DateTimeField(auto_now_add=True)
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, null=True)
class Meta:
default_related_name = 'response_messages'
def __str__(self):
return self.response_msg
#backend/serializers.py
class ListSerializer (serializers.Serializer):
user_id_id = serializers.IntegerField()
request_msg = serializers.CharField(max_length=100)
# request_msg_created_at = serializers.DateTimeField(read_only=True)
response_msg = serializers.CharField()
# response_msg_created_at = serializers.DateTimeField(read_only=True)
#backend/views.py
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Response_Messages, Request_Messages, User
from .serializers import ListSerializer
from django.db import connection
#api_view(['GET', 'POST'])
def chatbot(request):
if request.method == 'GET':
cursor = connection.cursor()
query_set = cursor.execute("SELECT backend_request_messages.user_id_id, backend_request_messages.request_msg as request_msg,backend_response_messages.response_msg as response_msg FROM backend_request_messages,backend_response_messages Where backend_request_messages.user_id_id=backend_response_messages.user_id_id=1 ")
columns = [column[0] for column in query_set.description]
results = []
for row in query_set.fetchall():
results.append(dict(zip(columns, row)))
serializer = ListSerializer(results)
return Response(serializer.data)
About serializers, You should refer to the docs (they're awesome and explain it best).
To give you a direction, I like to create a serializer for every model and if it's related to another model, I refer that in serializer, that way, You can easily customize behavior for each model (although not the only way at all).
So, about serializing I would do the following (notice my comments as well):
from django.contrib.auth.models import User
class User(User):
# Your user class, except, it should inherit Django's User/AbstractUser class.
class RequestMessages(models.Model):
request_msg = models.CharField(max_length=100)
request_msg_created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, related_name='requests_msg')
# NOTICE THE NEW RELATED NAME, WE'LL USE IT LATER.
class Meta:
default_related_name = 'request_messages'
def __str__(self):
return self.request_msg
class ResponseMessages(models.Model):
response_msg = response_msg = models.CharField(max_length=400)
response_msg_created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='responses_msg')
def __str__(self):
return self.response_msg
class RequestMsgSerializer(serializers.ModelSerializer):
# Specify what ever you like...
class Meta:
model = RequestMessages
fields = # Whatever you like to serialize.
class ResponseMsgSerializer(serializers.ModelSerializer):
class Meta:
model = ResponseMessages
fields = # Whatever you want serialized.
class UserSerializer(serializers.ModelSerializer):
# Using required = False will cause that every time you create a user they don't have to own messages.
requests_msg = RequestMsgSerializer(many=False, required=False)
responses_msg = ResponseMsgSerializer(many=False, required=False)
class Meta:
model = User
field = # Same as above ..
About your query, using raw SQL in Django is rear, usually, in most cases the Django built-in ORM will do the job and usually faster and better than you.
In your case, if you'll call your query like this for exmaple:
query_set = User.objects.filter(user=request.user)
the QuerySet object created will hit the DB one for the user object and X queries for all the associated messages with said user, so expensive.
But no need for a custom query with joins and stuff like that, Django has prefetch_related and select_related.
exmaple:
query_set = User.objects.filter(user=request.user).prefetch_related('requests_msg')
will reduce all the queries made for request messages associated to a user to only one!
Recap:
I wrote a lot because I'm still learning this stuff myself self and if you teach others you got it!
Refer to DRF's docs about serializers (there's even a dedicated section for nested serializers) and API Views, they really great.
Refer to Django's docs about prefetch related, select related and queries in general, again, Amazing docs that cover everything.
Don't just copy my code or anyone else's, there's no problem with that, just make sure you understand it first if not, you're bound to get stuck with it again!

Use existing sql-alchemy model class as flask-restplus api.model?

I am developing a CRUD application using vue.js and vuetify as frontend (view) and python flask-resplus and sqlAlchemy as backend (controler and model).
app/main/model/person.py
from sqlalchemy import Column, Integer, String, Date
from app.main.repository.base_repository import Base
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String)
date_of_birth = Column(Date)
def __init__(self, name=None, date_of_birth=None):
if name is not None:
self.name = name
if date_of_birth is not None:
self.date_of_birth = date_of_birth
app/main/controller/person_controller.py
from flask_restplus import Namespace, fields, Resource, cors
from flask import request
from app.main.service.person_service import PersonService
from app.main.model.person import Person
api = Namespace('persons', description='Person related operations')
persServ : PersonService = PersonService()
model = api.model('Person', {
'id': fields.Integer,
'name': fields.String,
'date_of_birth': fields.Date
})
#api.route("/persons/all")
class PersonList(Resource):
#api.marshal_with(model)
def get(self, **kwargs):
return persServ.get_persons()
# return Person(name="Test", date_of_birth=date(1984, 10, 20))
#api.route("/person/<int:id>")
class PersonReturn(Resource):
#api.marshal_with(model)
def get(self, id):
return persServ.get_person(id)
#api.route("/person/<int:id>")
class PersonUpdate(Resource):
#api.marshal_with(model)
def put(self, id):
data = request.json
#TODO marshalling currently still error
return persServ.update_person(pers=data)
#api.route("/person")
class PersonCreate(Resource):
#api.marshal_with(model)
def post(self):
data = request.json
#TODO: check why person is not correctly linked to sql alchemy when reated here, make sure that model.Person is created
# pers = Person()
# pers.name = data['name']
# persServ.create_person(pers)
return persServ.create_person_02(data['name'])
#api.route("/person/<int:id>")
class PersonDelete(Resource):
def delete(self, id):
persServ.delete_person(id)
return '', 204
Questions also after implementing a spring java backend :
1) Is it necessary to provide an api.model in person_controller.py or can I annotate properties in person.py? Could you kindly guide me to a tutorial annotating an existing object?
2) Naming conventions flask api classes: In my eyes it definitely makes sense to use PersonList and Person instead of PersonUpdate, PersonCreate to ease API documentation as described in the flask-resPlus full example: https://flask-restplus.readthedocs.io/en/stable/example.html
However, I already have an existing Person class in person.py. I am thinking about having two person classes, a Person(Resource) in person_controller.py for Post, Put and Get operations and a Person in person.py as domain specific person object. However, I do not want to cause confusion and adhere to naming and orgainization good practices. What is the suggested naming approach? Is there a good example tutorial?

Resources