I am getting NOT NULL constraint error even after passing the data from the form. I am using ajax for posting the data. I checked both the request.POST and network request tab. Both shows the store_contact_number has data in it.
Here is my code
class ListStore(FormView):
form_class = StoreForm
template_name = 'Store/list-store.html'
def form_invalid(self, form):
if self.request.is_ajax():
print('ajax form error', form.errors)
response = {
'error': form.errors
}
return JsonResponse(response, status=400)
else:
return super(ListStore, self).form_invalid(form.errors)
def form_valid(self, form):
success_message = "Thank you for listing your store. We Welcome you."
store, created = Store.objects.get_or_create(merchant=self.request.user)
if self.request.is_ajax():
response = {
'result': success_message
}
return JsonResponse(response)
else:
message.success(self.request, success_message)
return self.render_to_response(self.get_context_data())
class Store(models.Model):
merchant = models.ForeignKey(User, blank=False, null=False)
token = models.CharField(default=token_generator, max_length=20, unique=True, editable=False)
name_of_legal_entity = models.CharField(max_length=250, blank=False, null=False)
pan_number = models.CharField(max_length=20, blank=False, null=False)
registered_office_address = models.CharField(max_length=200)
name_of_store = models.CharField(max_length=100)
email = models.EmailField(blank=False, null=False)
store_contact_number = models.PositiveIntegerField(blank=False, null=False)
$('.list-store-form').on('submit', function(event) {
event.preventDefault(); // preventing from the brwoser default behavior for form submission
var form = $(this);
console.log(form);
$.ajax({
async: true,
url: form.attr('action'),
data: form.serialize(),
type: 'POST',
dataType: 'json',
headers: {
'X-CSRFToken': window.csrf_token
},
success: function(data) {
if (data.form_is_valid) {
alert('Store Listed');
}
// $('.display').html(
// "<div class='ui floating message'> <i class='close icon'></i>" + data.result + '</div>'
// );
},
The error is shown in the store_contact_number as
IntegrityError at /list/store NOT NULL constraint failed:
Store_store.store_contact_number
<QueryDict: {'store_contact_number': ['98489678'], 'email': ['admin#gmail.com'], 'registered_office_address': ['Delhi'], 'csrfmiddlewaretoken': ['kSaPMgNt'], 'name_of_store': ['BigMart'], 'pan_number': ['98713962'], 'name_of_legal_entity': ['BigMart Pvt Ltd']}>
Form
class StoreForm(forms.ModelForm):
class Meta:
model = Store
exclude = ('is_active', 'token', 'merchant',)
You need to add store = form.save() at the start of your form_valid method and get rid of the line store, created = Store.objects.get_or_create(merchant=self.request.user).
EDIT: Actually it's not that simple because the form.save() will also fail due to not having the merchant field specified. You'll need to either need to override either the __init__ or save method of your form so you can pass in the merchant.
Something like:
def save(self, merchant, *args, **kwargs):
commit = kwargs.pop('commit', True)
kwargs['commit'] = False
store = super().save(*args, **kwargs)
store.merchant = merchant
if commit:
store.save()
return store
then call it using:
def form_valid(self, form):
store = form.save(merchant=self.request.user)
success_message = "Thank you for listing your store. We Welcome you."
if self.request.is_ajax():
response = {
'result': success_message
}
return JsonResponse(response)
else:
message.success(self.request, success_message)
return self.render_to_response(self.get_context_data())
Related
I tried to make a pure api Django and save some data in my database for practicing.
But I have some problems when I try to save data.
For example
I tried this code for the post method
def post(self, request, *args, **kwargs):
form = UpdateModelForm(self.request.POST)
if form.is_valid():
obj = form.save(commit=True)
obj_data = obj.serialize()
return self.render_to_response(obj_data, status=201)
if form.errors:
data = json.dumps(form.errors)
return self.render_to_response(data, status=400)
data = {"message": "Not Allowed"}
return self.render_to_response(data, status=400)
And here is my Form validation
from django import forms
from .models import Update as UpdateModel
class UpdateModelForm(forms.ModelForm):
class Meta:
model = UpdateModel
fields = [
'user',
'content',
'image'
]
And the model
class UpdateQuerySet(models.QuerySet):
#def serialize(self):
# qs = self
# return serialize('json', qs, fields=('user', 'content', 'image'))
def serialize(self):
list_values = list(self.values("user", "content", "image", "id"))
return json.dumps(list_values)
class UpdateManager(models.Manager):
def get_queryset(self):
return UpdateQuerySet(self.model, using=self._db)
# Create your models here.
class Update(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
content = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to=upload_update_image, blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
objects = UpdateManager()
def __str__self(self):
return self.content or ""
def serialize(self):
try:
image = self.image.url
except:
image = ""
data = {
"id": self.id,
"user": self.user,
"content": self.content,
"image": image
}
data_json = json.dumps(data)
return data_json
My problem arises when I try to use the method, I get this dubious error.
What do you think it is?
{"user": ["Select a valid choice. That choice is not one of the available choices."]}
Try with :
data = {
"id": self.id,
"user": self.user.id,
"content": self.content,
"image": image
}
NB : Pass the user ID instead of the user object.
I'm trying to send data to my Django/DRF backend via Axios POST requests. Some of them have been working, but some of them haven't been. This snippet, for example, works fine:
axiosInstance.post(
`/api/notes/`,
{
title,
content: serialize(editorContent),
}
)
This one, however, has been causing problems:
axiosInstance.post(
`/api/notebooks/`,
{
name: newNotebookName,
user,
}
)
In the second snippet, the name attribute gets through to the backend, but for some reason the user attribute does not. I tried using console.log to see if user is undefined at the moment it's sent off to the backend and it isn't.
I tried to change the way I define the keys in the object I send to the backend so that they're not dynamic like in this answer and the result hasn't changed.
The user variable is a string containing an email address, like 'foobar#gmail.com'. I thought maybe Django blocked it by default for security reasons, but I split the string at the # and tried sending the first part only and it didn't work either.
I also tried defining a new variable so that the type of user is string rather than string | undefined and it still doesn't work. The value of title is also string and it gets through to the backend (it's defined in the validated_data object my serializer sees in its create() method). So I'm at a loss as to what this could be. Here are some of my files:
axiosAPI.ts
import axios from 'axios'
const baseURL = 'http://localhost:8000/'
export const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
'xsrfHeaderName': 'X-CSRFTOKEN',
'xrsfCookieName': 'csrftoken',
'Authorization': localStorage.getItem('access_token') ? 'JWT ' + localStorage.getItem('access_token') : null,
'Content-Type': 'application/json',
'accept': 'application/json',
}
})
axiosInstance.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config
// Prevent infinite loops if login credentials invalid
if (error.response.status === 406 && originalRequest.url === baseURL + 'auth/token/obtain') {
window.location.href = '/login/'
return Promise.reject(error);
}
// Respond to expired refresh tokens
if (error.response.data.code === 'token_not_valid' && error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken) {
const tokenObj = JSON.parse(atob(refreshToken.split('.')[1]))
const currentTime = Math.ceil(Date.now() / 1000)
if (tokenObj.exp > currentTime) {
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest);
})
.catch(err => {
console.log(err)
throw err;
})
} else {
console.log('Refresh token is expired.')
window.location.href = '/login/'
}
} else {
console.log('Refresh token not available.')
window.location.href = '/login/'
}
}
// Respond to invalid access tokens
if (error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest)
}).catch(err => {
console.log(err)
throw err;
})
}
}
)
notebooks/serializers.py
import bleach
import json
from rest_framework import serializers
from authentication.models import ErsatzNoteUser
from .models import Note, Notebook
class NoteSerializer(serializers.ModelSerializer):
note_id = serializers.SlugField(source='id', read_only=True, required=False)
title = serializers.JSONField(required=False)
content = serializers.CharField(required=False)
notebook = serializers.PrimaryKeyRelatedField(read_only=True, required=False)
date_modified = serializers.DateField(read_only=True, required=False)
date_created = serializers.DateField(read_only=True, required=False)
def create(self, validated_data):
title = json.dumps(validated_data['title'])
# Workaround to fix a currently unpatched bug in Slate
# that occurs when an editor's contents begin with a list
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
response_data = {
'title': title,
'content': content,
}
return Note.objects.create(**response_data)
def update(self, instance, validated_data):
instance.title = json.dumps(validated_data['title'])
# See the above comment in the 'create' method
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
instance.content = content
instance.save()
return instance
class Meta:
model = Note
fields = [ 'note_id', 'title', 'content', 'notebook', 'date_modified', 'date_created' ]
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
# notebook_data = {
# 'name': validated_data['name'],
# 'user': ErsatzNoteUser.objects.get(email=validated_data['user']),
# }
print(validated_data)
return Notebook.objects.create(name=validated_data['name'], user=ErsatzNoteUser.objects.get(email=validated_data['user']))
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
notebooks/views.py
import json
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import NoteSerializer, NotebookSerializer
from .models import Note, Notebook
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
def get_queryset(self):
return self.request.user.notes.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NoteSerializer(instance)
title = json.loads(serializer.data['title'])
response_data = {
'note_id': serializer.data['note_id'],
'title': title,
'content': serializer.data['content'],
'notebook': serializer.data['notebook'],
'date_modified': serializer.data['date_modified'],
'date_created': serializer.data['date_created'],
}
return Response(response_data)
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
notebooks/models.py
from django.db import models
from django.contrib.auth.models import User
from jsonfield import JSONField
from django.conf import settings
from .helpers import generate_slug
class Note(models.Model):
""" Represents an individual note. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
title = JSONField(null=True)
content = models.TextField(null=True)
notebook = models.ForeignKey('Notebook', related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
# Temporary expedients for the sake of development
if not self.notebook:
self.notebook = Notebook.objects.get(id='YyOzNhMFMPtN8HM')
super(Note, self).save(*args, **kwargs)
def __str__(self):
return self.id
class Meta:
ordering = [ '-date_modified', '-date_created', ]
class Notebook(models.Model):
""" A collection of individual notes. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
name = models.CharField(max_length=64, default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notebooks', on_delete=models.CASCADE)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
super(Notebook, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
ordering = [ '-date_modified', '-date_created', 'name' ]
Please don't hesitate to ask if you'd like further info.
EDIT: Changed NotebookViewSet so that it has a retrieve() method, the result hasn't changed:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NotebookSerializer(instance)
response_data = {
'name': serializer.data['name'],
'user': serializer.data['user'],
}
return Response(response_data)
In your example your NotebookViewSet class does not have a retrieve method.
It turns out that under the hood, DRF considers ForeignKey fields to be read_only. Meaning it's solely to be used for representing the object, and shouldn't be part of any create or update process. According to the documentation:
Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.
Source.
But this didn't matter to my database, which requires me to identify the user that corresponds to each new Notebook at the time of its creation, or else a NOT NULL constraint is violated. So I just overwrote the default ModelViewSet create() method to pass on the user string manually to my NotebookSerializer as part of context, allowing me to use it to get the corresponding user object in the database and assign it to the newly created Notebook. I went about it like this:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def perform_create(self, serializer):
serializer.save()
def create(self, request, *args, **kwargs):
serializer = NotebookSerializer(data=request.data, context={ 'request': request })
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
return Notebook.objects.create(
user=ErsatzNoteUser.objects.get(email=self.context['request'].user),
**validated_data
)
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
That's all it took.
I’m trying to build a web app using Django and I want to upload a file (a pdf or word document) using ajax (the ajax module in jquery).
I’m building my API using the django-rest-framework.
I’ve pretty much tried to do what every site says but can’t seem to get around it.
Has anyone done this?
My views:
def contract_post(request):
contract = Contract()
if request.method == 'POST':
serializer = ContractSerializer(contract, data=request.data)
data = {}
if serializer.is_valid():
serializer.create(validated_data=request.data)
handle_uploaded_file(request.FILES['atasament'])
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def handle_uploaded_file(f):
with open(f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
My serializer (it's a nested serializer):
class ContractSerializer(serializers.ModelSerializer):
client = ClientSerializer(read_only=False)
termen = serializers.DateField(format='%Y-%m-%d', default=datetime.date.today)
atasament = serializers.FileField(max_length=None, allow_empty_file=True)
class Meta:
model = Contract
fields = (
'pk',
'tip',
'nr_data',
'obiectul',
'termen',
'atasament',
'client',
)
def create(self, validated_data):
client = validated_data.pop('client')
client_instance, created = Client.objects.get_or_create(**client)
contract_instance = Contract.objects.create(**validated_data, client=client_instance)
return client_instance
My model:
class Contract(models.Model):
TIPURI = (
('P', 'Proiectare'),
('E', 'Executie'),
('D', 'Documentatie'),
)
tip = models.CharField(max_length=300, choices=TIPURI)
nr_data = models.CharField(max_length=50)
obiectul = models.TextField()
termen = models.DateField(default=datetime.date.today)
atasament = models.FileField(upload_to='main/documents/', blank=True, null=True)
client = models.ForeignKey(Client, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.nr_data
class Meta:
verbose_name_plural = "Contracte"
My JS:
$('#form_add').submit(function (event) {
event.preventDefault();
let input_atasament = document.getElementById('input_atasament');
let client_pk = $("#input_clienti").val();
let nr_data = "test";
let tip = $("#input_tip").val();
let obiectul = $("#input_obiectul").val();
let termen = $("#input_termen").val();
const url = \/api/contract-post/`;let client_url = `/api/client-get/${client_pk}`;let client = $.ajax({url: client_url,async: false,dataType: 'json'}).responseJSON;`
let data = new FormData();
data.append('nr_data', "test");
data.append('tip', tip);
data.append('obiectul', obiectul);
data.append('termen', termen);
data.append('atasament', input_atasament.files[0]);
data.append('client', JSON.stringify(client));
var action = function(d) {
console.log(d);
};
$.ajax({
url: url,
headers: {
'X-CSRF-TOKEN': csrftoken,
'Content-Type': 'multipart/form-data',
},
data: data,
type: "POST",
contentType: false,
processData: false,
success: action,
error: action
});
});
And what error I get now:
"Multipart form parse error - Invalid boundary in multipart: None"
Im using angular/typescript where I upload a form of data to my Django REST API. With the data all is correct, because I can log my whole form and I get back all my data. But when it comes to upload the data I get this error:
{src: ["No file was submitted."], tag: ["This field is required."]}
so somehow it does not recognize my data because Im actually submitting data.
frontend code
upload.service
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'multipart/form-data'})
};
...
uploadPost(src: File, tag: string) {
return this.http.post<any>(`{this.url}/posts/`, {src, tag}, httpOptions);
}
post.page
ngOnInit() {
this.storage.get('image_data').then((imageFile) => {
console.log(imageFile)
this.categoryForm.patchValue({
'image': this.storage.get('image_data')
});
});
this.categoryForm = new FormGroup({
'category': new FormControl('', Validators.compose([
Validators.maxLength(25),
Validators.minLength(1),
Validators.required
])),
'image': new FormControl(null),
});
apiSubmit() {
console.log('logged')
console.log(this.f.image);
this.submitted = true;
if (this.categoryForm.invalid) {
return;
}
this.isLoading = true;
this.loadingEl.present();
this.uploadService.uploadPost(
this.f.image,
this.f.category
)
.pipe(tap(x => this.loadingEl.dismiss())
)
.subscribe(
data => {
this.router.navigate(['one']);
},
error => {
this.error = error;
}
);
}
Django:
models.py
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='posts', on_delete=models.CASCADE)
src = models.ImageField(blank=False, null=False, editable=False, upload_to=utils.get_file_path,)
date_posted = models.DateTimeField(auto_now_add=True, editable=False)
last_modified = models.DateTimeField(auto_now=True)
when = models.FloatField(blank=True, null=True)
lock = models.BooleanField(default=False)
des = models.CharField(
max_length=100,
validators=[
RegexValidator(
regex='^[-_a-zA-Z0-9\.]+$',
message='only 1 to 20 characters and only letters, numbers, point, minus and underscore are allowed',
),
])
view.py
#swagger_auto_schema(request_body=doc_serializer.CreatePostDummy, responses={201: PostSerializer})
def create(self, request, *args, **kwargs):
"""permission required: authenticated. Note: the schema is not json it's multipart"""
return super().create(request, *args, **kwargs)
doc_serializers.py
class CreatePostDummy(serializers.HyperlinkedModelSerializer):
`enter code here`user = serializers.HyperlinkedRelatedField(required=False, read_only=True, view_name='user-detail')
src = serializers.CharField(required=True, help_text='Not Char but image upload field')
I send data from html fields via AJAX
var dataToSend = { some1 : $('.name1').html().replace(/[\n\r]/g, '').trim(),
some2 : $('.name2').html().replace(/[\n\r]/g, '').trim(),
some3 : $('.name3').html().replace(/[\n\r]/g, '').trim(),
some4 : $('.name4').html().replace(/[\n\r]/g, '').trim() }
if (dataToSend) {
$.ajax({
url: 'do/smth',
data: dataToSend,
async: false,
type: 'POST',
success: function() {
alert('draft is saved')
}
});
}
Then i receive POST request which does contain data but my model saves only when all arguments are existing, so for example if i get only {some1:"value1",some2:"value2"} my django view WONT save it.
The question is: how can i possibly save data into db, if my fields are empty, as you can see i have blank=True in my model, but it does not help
model
class Draft(models.Model):
name = models.CharField(_('company name'), max_length=100, blank=True)
slug = models.SlugField(max_length=255, blank=True)
pitch = models.CharField(_('pitch'), max_length=140, blank=True)
url = models.CharField(_('url'), max_length=255, null=True, blank=True)
thesis = models.TextField(_('thesis'), blank=True)
def __unicode__(self):
return self.name
views
def save_from_ajax_post(request):
data = dict(request.POST.items())
draft = Draft.objects.create(data)
draft.save()
return HttpResponse(json.dumps({'status': 1}), content_type="application/json")