Problem using unique_together and saving authenticated user - python

There is a problem in my code for two fields entry uniqueness checking.
I defined a model with unique_together to check uniqueness of a field records for each user, but it accepts duplicated entry added by that user.
model.py
from django.db import models
from django.contrib.auth.models import User
class UserItem(models.Model):
definer = models.ForeignKey(User, on_delete=models.CASCADE)
item_name = models.CharField(max_length=50)
.
.
class Meta:
unique_together = ("definer", "item_name")
views.py
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView
class RecordCreateView(LoginRequiredMixin, CreateView):
model = UserItem
template_name = 'item_new.html'
#excluding "definer" field and inserting its value by form_valid
fields = ['item_name', . . .]
def form_valid(self, form):
form.instance.definer = self.request.user
return super().form_valid(form)
I expect warning and preventing users to add new record with the same "item_name" added before by themselves, but it accepts them (without warning).
When I replace "definer" with other fields, it works fine and warns for duplicate records. Additionally when records added by admin, it works and there will be the expected warning.
I guess, this problem is for that the authenticated user is inserted as "definer" by "def form_valid" after the "unique_together = ("definer", "item_name")" has done its role. On the other hand, uniqueness checking in done when the "definer" is empty.
What should I do to solve this problem?
Edit: Adding full model
```` Full Model in model.py
class UserItem(models.Model):
item_type = models.CharField(max_length=12, verbose_name='Item type')
item_name = models.CharField(max_length=50)
bound = models.CharField(null=True, blank=True, default=None, max_length=4, verbose_name='Bound')
price = models.FloatField(default=0)
maximum_use = models.FloatField(default=0, verbose_name='Maximum use (%)’)
matterial = models.FloatField(null=True, blank=True, default=None, verbose_name='matterial (%)')
energy = models.FloatField(null=True, blank=True, default=None, verbose_name='energy (kcal/k)')
definer = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return "{}, name: {}, definer: {}".format(self.item_type, self.item_name, self.definer,)
def get_absolute_url(self):
return reverse('profile')
class Meta:
unique_together = ("definer", "item_name")
````
```` views.py after #Pedro suggestion to edit
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.contrib.auth.models import User
from django.contrib.auth.mixins import LoginRequiredMixin
class RecordCreateView(LoginRequiredMixin, CreateView):
model = UserItem
template_name = 'item_new.html'
fields = ['item_name', 'matterial', 'energy',]
def get_success_url(self):
return reverse('profile')
def form_valid(self, form):
user_item = form.save(commit=False)
user_item.definer = self.request.user
user_item.item_type = 'required'
user_item.bound = 'min'
try:
user_item.save()
except IntegrityError:
form.add_error('item_name', 'Item name is repeated')
return self.form_invalid(form)
return HttpResponseRedirect(self.get_success_url())
````

Thanks to #Pedro's very helpful hints; Lastly I could solve my problem by some changes in his code.
Also I removed this part in model.py:
"class meta:
unique_together = ("definer", "item_name")"
````views.py
class RecordCreateView(LoginRequiredMixin, CreateView):
model = UserItem
template_name = 'item_new.html'
fields = ['item_name', 'matterial', 'energy',]
def form_valid(self, form):
user_items = form.save(commit=False)
item_name = user_items.item_name
qs = UserItemComposition.objects.filter(definer=self.request.user, item_name=item_name)
if qs.exists():
form.add_error('item_name', 'Item name is repeated')
return self.form_invalid(form)
form.instance.definer = self.request.user
return super().form_valid(form)
````

The problem is you are adding the definer after the form is validated. You can pass the request.user as the initial data, like this:
class RecordCreateView(LoginRequiredMixin, CreateView):
model = UserItem
template_name = 'item_new.html'
#excluding "definer" field and inserting its value by form_valid
fields = ['item_name', 'definer', ...]
def get_initial(self):
initial = super().get_initial()
initial['definer'] = self.request.user
return initial
Now you don't need to override form_valid.
Edit: If you don't want the definer in the form fields you can do this:
class RecordCreateView(LoginRequiredMixin, CreateView):
model = UserItem
template_name = 'item_new.html'
fields = ['item_name', ...]
def form_valid(self, form):
user_item = form.save(commit=False)
user_item.definer = self.request.user
try:
user_item.save() # should raise an exception if unique_together constrain fails
except ValidationError:
form.add_error('item_name', 'Item name is repeated') # add custom error to form
return self.form_invalid(form) # return the invalid form
return HttpResponseRedirect(self.get_success_url())

Related

no such column: Blog_post.category_id

I’m trying to create a category for each post.
I made another class with the same models.py and in the same class Post I made a category = models.ForeignKey
But it keeps showing me this error when I run the server:
(no such column: Blog_post.category_id)
Ps: I did run the makemigrations and the migrate command.
The tutorial I followed just added the model as it is in models.py but should I also make a function for the views.py or its just a model problem ?
models.py
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, default="Some random thing")
werkstoffnummer = models.CharField(max_length=100)
def __str__(self):
return self.werkstoffnummer
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.pk})
views.py
from random import choices
from unicodedata import category
from django import forms
from django.shortcuts import get_object_or_404, render, get_list_or_404, HttpResponse
from django.conf import settings
from django.http import HttpResponse, Http404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from django.views.generic import (ListView, DetailView, CreateView, UpdateView, DeleteView)
from .models import Post, Category
from django.db.models import Q
import pandas as pd
import json
import os
def home(request):
context = {"posts": Post.objects.all()}
return render(request, "Blog/home.html", context)
def werkstoffdaten(request):
context = {"posts": Post.objects.all()}
return render(request, "Blog/werkstoffdaten.html", context)
class PostListView(ListView):
model = Post
template_name = "Blog/werkstoffdaten.html" # <app>/<model>_<viewtype>.html
context_object_name = "posts"
ordering = \["-date_posted"\]
paginate_by = 100
class PostListView(ListView):
model = Post
template_name = "Blog/home.html" # <app>/<model>_<viewtype>.html
context_object_name = "posts"
ordering = \["-date_posted"\]
paginate_by = 100
class UserPostListView(ListView):
model = Post
template_name = "Blog/user_posts.html" # <app>/<model>_<viewtype>.html
context_object_name = "posts"
paginate_by = 100
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get("username"))
return Post.objects.filter(author=user).order_by("-date_posted")
class PostDetailView(DetailView):
model = Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = \[
"werkstoffnummer", "werkstoffbezeichnung",
\]
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = \[
"werkstoffnummer", "werkstoffbezeichnung",
\]
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = "/"
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def Periodic_Table(request):
return render(request, "Blog/Periodic_Table.html")
def user_manual(request):
return render(request, "Blog/user_manual.html")
def about(request):
return render(request, "Blog/about.html", {"title": "About"})
def search_post(request):
if request.method == "POST":
searched = request.POST.get("searched")
posts = Post.objects.filter(
Q(werkstoffnummer=searched) | Q(werkstoffbezeichnung=searched) | Q(gruppe=searched)
)
return render(
request, "Blog/search_post.html", {"searched": searched, "posts": posts}
)
else:
return render(request, "Blog/search_post.html", {})
def download(request, path):
file_path = os.path.join(settings.MEDIA_ROOT, path)
if os.path.exists(file_path):
with open(file_path, 'rb') as fh:
response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
response\['Content-Disposition'\] = 'inline; filename=' + os.path.basename(file_path)
return response
raise Http404
OperationalError at /
https://imgur.com/a/lVDedeq
Delete default="Some random thing" in your category field, default value for foreign key of your Category model, must be a private key (pk (aka id (type == integer))), but not the string.
If you just delete this default value, it will work, or you can put default=1 as first object of your Category models would be default for your new Post objects
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
or
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1)

CreateView and UpdateView based class are not working in Django Project

I have Django in Which there is app called Listing and it's model as follows but CreateView and UpdateView are not woking for model.
I am also using mixins but I have implemented Custom User , so is that cuases a problem?
getting error
in get_form return form_class(**self.get_form_kwargs())
TypeError: 'ListingForm' object is not callable
Any other user is updating listing of others, UserPassesTestMixin not working
import uuid
from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model
User = get_user_model()
class Listing(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
...other fields
CreateView
class ListingCreateView(LoginRequiredMixin, CreateView):
model = Listing
form_class = ListingForm()
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
UpdateView
class ListingUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Listing
form_class = ListingForm()
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
def test_func(self):
listing = self.get_object()
#print(self.request.user, listing.owner)
if(self.request.user == listing.owner):
return True
else:
return False
forms.py
from django import forms
from .models import Listing
class ListingForm(forms.ModelForm):
class Meta:
model = Listing
fields = ['price', 'sqft', 'acre', 'title', 'description', 'address', 'city', 'state',
'country', 'zipcode', 'photo_main', 'photo_1', 'photo_2', 'photo_3', 'photo_4', 'photo_5']
Tysm for solving in advance!
You are missing form_class in your update and create view. You need to be add your forms name.
CreateView
class ListingCreateView(LoginRequiredMixin, CreateView):
model = Listing
form_class = your form name
fields = blah blah blah
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
UpdateView
class ListingUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Listing
form_class = your form name
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
def test_func(self):
listing = self.get_object()
#print(self.request.user, listing.owner)
if(self.request.user == listing.owner):
return True
else:
return False

How to solve the NOT NULL constraint failed Error in Django

I am getting an IntegrityError when I want to save a new course on my e-learning website. Of course, I have searched for a similar solution on StackOverflow but I couldn't find an appropriate way for my solution.
here are my models
UserAccount Model
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserAccount(AbstractUser):
email = models.EmailField(
max_length=255, verbose_name='email', unique=True)
username = models.CharField(max_length=255, unique=True)
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
Course Model
from django.db import models
from accounts.models import UserAccount
class Course(models.Model):
owner = models.ForeignKey(
UserAccount, related_name='courses_created', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug = models.SlugField()
description = models.TextField()
cover_photo = models.ImageField(upload_to="cover/", null=True, blank=True)
Also, my course form is here
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ['curriculum', 'title', 'description', 'cover_photo']
widgets = {
'description': forms.Textarea(attrs={'rows': 3})
}
So in my view, I like to send the list of my courses to the template and also my CourseForm() using the get_context_data method. The code is bellow
My class-based view
class OwnerListMixin(object):
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(owner=self.request.user)
class OwnerCourseMixin(OwnerListMixin, LoginRequiredMixin, PermissionRequiredMixin):
model = Course
fields = ['curriculum', 'title', 'description', 'cover_photo']
success_url = reverse_lazy('manage_course_list')
class ManageCourseListView(OwnerCourseMixin, ListView):
template_name = "courses_app/manage/course/list.html"
permission_required = "courses_app.view_course"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = CourseForm()
return context
but when I render the form in my template to save a new course I get the following Error
IntegrityError at /create/
NOT NULL constraint failed: courses_app_course.owner_id
CREATE VIEW
class CourseCreateView(OwnerCourseMixin, CreateView):
permission_required = "courses_app.add_course"
template_name = "courses_app/manage/course/form.html"
success_url = reverse_lazy("manage_course_list")
You need to set the user as the owner what currently is not happening. The form is being saved with the fields you have set. There are multiple ways of adding the user. You could do it this way.
from django.shortcuts import redirect
class CourseCreateView(OwnerCourseMixin, CreateView):
permission_required = "courses_app.add_course"
template_name = "courses_app/manage/course/form.html"
success_url = reverse_lazy("manage_course_list")
def form_valid(self, form):
instance = form.save(commit=False)
instance.owner = self.request.user
instance.save()
return redirect(self.get_success_url())

How to request user login in models or views

I am newbie in Django, and I can’t figure out how to get username through request. I’m working on vocabulary type site and I need that every entry user creates would have username.
Here is my models.py
from django.db import models
class EngDict(models.Model):
orig_word = models.CharField(max_length=500, null=False,blank=False, verbose_name='Слово')
translate = models.CharField(max_length=500,null=False,blank=False, verbose_name="Перевод")
remarks = models.TextField(null=True, blank=True, verbose_name="Примечания")
published_by = models.CharField(max_length=50, verbose_name="Добавлено")
published_date = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="Дата добавления")
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.PROTECT, verbose_name = 'Категория')
class Meta:
verbose_name = ("Перевод")
verbose_name_plural = "Англо-русский словарь"
ordering = ['-published_date']
This is views.py
from django.shortcuts import render
from django.template import loader
from .models import EngDict, Category
from django.views.generic.edit import CreateView
from .forms import EngDictForm
from django.urls import reverse_lazy
from django.views.generic import TemplateView, ListView, FormView
from django.db.models import Q
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login
class EngDictCreateView(CreateView):
template_name = 'dict/create.html'
form_class = EngDictForm
success_url = reverse_lazy('index')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
I see that I need some function like def user_name(request) , but I can’t understand where i should write it and what must be inside of it. I need that published_by variable was automatically filled in with user login
Added exclude in forms.py
from django.forms import ModelForm
from .models import EngDict
from django import forms
class EngDictForm (ModelForm):
def clean_orig_word(self):
data = self.cleaned_data['orig_word']
if EngDict.objects.filter(orig_word = data).count():
raise forms.ValidationError("ТАКОЕ СЛОВО УЖЕ ЕСТЬ!")
return data
class Meta:
model = EngDict
fields = ('orig_word', 'translate', 'remarks', 'published_by')
exclude =['published_by']
Please do not use a CharField for that. If later the user changes their username, then it refers to a user that no longer exists. To refer to another object, one uses a relation field like a ForeignKey [Django-doc], OneToOneField [Django-doc], or , ManyToManyField [Django-doc]. Here it looks like a ForeignKey is what you are looking for:
from django.conf import settings
class EngDict(models.Model):
orig_word = models.CharField(max_length=500, null=False,blank=False, verbose_name='Слово')
translate = models.CharField(max_length=500,null=False,blank=False, verbose_name='Перевод')
remarks = models.TextField(null=True, blank=True, verbose_name='Примечания')
published_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
editable=False,
verbose_name='Добавлено'
)
published_date = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Дата добавления')
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.PROTECT, verbose_name = 'Категория')
class Meta:
verbose_name = ('Перевод')
verbose_name_plural = 'Англо-русский словарь'
ordering = ['-published_date']
In the form you should exclude published_by and in the CreateView, you can then "patch" the object with:
from django.contrib.auth.mixins import LoginRequiredMixin
class EngDictCreateView(LoginRequiredMixin, CreateView):
template_name = 'dict/create.html'
form_class = EngDictForm
success_url = reverse_lazy('index')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
def form_valid(self, form):
form.instance.published_by = self.request.user
return super().form_valid(form)
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].

Django - Why am I getting this IntegrityError: NOT Null constraint failed: restaurants_restaurantlocation.owner_id?

I am trying to make a website where people can find restaurants and menu items based on their pickiness. Currently I am trying to do it so when I add something to the restaurants through forms I have it associated to an user but when I submit the form I get this error:
IntegrityError
NOT Null constraint failed: restaurants_restaurantlocation.owner_id
Here is my forms.py:
from django import forms
from .models import RestaurantLocation
from .validators import validate_category
class RestaurantCreateForm(forms.Form):
name = forms.CharField()
location = forms.CharField(required = False)
category = forms.CharField(required = False)
def clean_name(self):
name = self.cleaned_data.get("name")
if name == "Hello":
raise forms.ValidationError("This is an invalid name. You stubid boy.")
return name
class RestaurantLocationCreateForm(forms.ModelForm):
#email = forms.EmailField()
#category = forms.CharField(validators = [validate_category], required = False)
class Meta:
model = RestaurantLocation
fields = [
"name",
"location",
"category"
]
def clean_name(self):
name = self.cleaned_data.get("name")
if name == "Hello":
raise forms.ValidationError("This is an invalid name. You stubid boy.")
return name
My models.py:
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save, post_save
from .utils import unique_slug_generator
from .validators import validate_category
# Create your models here.
User = settings.AUTH_USER_MODEL
class RestaurantLocation(models.Model):
owner = models.ForeignKey(User)
name = models.CharField(max_length=120)
location = models.CharField(max_length=120, null=True, blank=True)
category = models.CharField(max_length=120, null=True, blank=True, validators= [validate_category])
slug = models.SlugField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
#property
def title(self):
return self.name #obj.title
def rl_pre_save_reciever(sender, instance, *args, **kwargs):
instance.category = instance.category.capitalize()
if not instance.slug:
instance.slug = unique_slug_generator(instance)
pre_save.connect(rl_pre_save_reciever, sender=RestaurantLocation)
My views.py:
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView
from django.utils.datastructures import MultiValueDictKeyError
from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation
# Create your views here
def restaurant_createview(request):
form = RestaurantLocationCreateForm(request.POST or None)
errors = None
if form.is_valid():
# customise
# like a pre save
form.save()
# like a post save
return HttpResponseRedirect("/restaurants/")
if form.errors:
errors = form.errors
template_name = "restaurants/form.html"
context = {"form" : form, "errors" : errors}
return render(request, template_name, context)
def restaurant_listview(request):
template_name = "restaurants/restaurants_list.html"
queryset = RestaurantLocation.objects.all()
context = {
"object_list": queryset
}
return render(request, template_name, context)
class RestaurantListView(ListView):
def get_queryset(self):
slug = self.kwargs.get("slug")
if slug:
queryset = RestaurantLocation.objects.filter(
Q(category__iexact = slug) |
Q(category__icontains = slug)
)
else:
queryset = RestaurantLocation.objects.all()
return queryset
class RestaurantDetailView(DetailView):
queryset = RestaurantLocation.objects.all()
class RestaurantCreateView(CreateView):
form_class = RestaurantLocationCreateForm
template_name = "restaurants/form.html"
success_url = "/restaurants/"
If you need any other piece of code please ask, thank you
Looking at your RestaurantLocation model, you have a foreign key into the User table:
class RestaurantLocation(models.Model):
owner = models.ForeignKey(User)
By default this can't be empty (this is what "NOT null constraint" means). And indeed it looks like your form does not do anything to fill in a restaurant owner, so you will get a database constraint error when you try to commit.
"This is an invalid name. You stubid boy."
Not a very nice thing to say to your users.

Categories