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())
Related
New to Django and making a restuarant booking system. I have everything going to my database but now i'm trying to return the info back to the user, ideally in a similar form view to what i have, so they can edit or delete the booking.
My code so far is as follows:
models.py:
class Reservations(models.Model):
reservation form categories and attributes
name = models.CharField(max_length=50)
phone_number = models.CharField(validators=[phoneNumberRegex], max_length=16,
unique=True)
email = models.EmailField()
date = models.DateField()
time = models.CharField(choices=time_options, default="12pm", max_length=10)
number_of_party = models.IntegerField(choices=party_size, default=1)
reservation_id = models.UUIDField(primary_key=True, default=uuid.uuid4,
editable=False, max_length=15)
class Meta:
ordering = ['date']
verbose_name = 'Reservation'
verbose_name_plural = 'Reservations'
def __str__(self):
return self.name
Then my form looks like this:
form.py:
class ReservationForm(forms.ModelForm):
class Meta:
model = Reservations
fields = ['name', 'phone_number', 'email', 'date', 'time', 'number_of_party']
widgets = {
'date': DatePickerInput(format='%d-%m-%Y'),
}
Then where I think i'm really getting lost is with the views.
view.py:
class ReservationsFormView(CreateView):
model = Reservations
template_name = "reservations/reservations.html"
form_class = ReservationForm
success_url = "reservation_complete/"
def form_valid(self, form):
return super().form_valid(form)
class EditReservationView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Reservations
template_name = "reservations/edit_reservations.html"
form_class = ReservationForm
success_url = "reservation_complete/"
def test_func(self):
return self.request.user == self.get_object().user
class ReservationCompleteView(CreateView):
template_name = "reservations/reservation_complete.html"
success_url = "/reservation_complete/"
form_class = ReservationForm
model = Reservations
class ReservationAccountView(TemplateView):
template_name = "reservations/reservations_account.html"
class DeleteReservationView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
""" A view to delete an reservation """
model = Reservations
success_url = "/reservations/"
def test_func(self):
return self.request.user == self.get_object().user
So basically if someone could help me extract the data and present back to the user in a html file I would greatly appreciate it.
A lot of the material online isn't using class based views but i start that way so sticking it out.
Sorry for the verbose code.
Thanks in advance
While Django's Edit, Create, etc views are quick and helpful, I find them hard to manipulate, especially if I haven't used them in a while. I recommend getting used to writing your own views, that way you can set whatever action you want the view to perform.
Let's say your URL looks like this - edit/4281ee81-a12a-4a0f-8928-2d9639903bd7/
forms.py
class EditReservationForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
views.py
class EditReservationView(LoginRequiredMixing, View):
def get(self, request, *args **kwargs):
reservation = Reservations.objects.get(uuid=kwargs['uuid'])
form = EditReservationForm(initial={"name": reservation.name, "email": reservation.email})
return render(request, "reservation/edit-reservation.html", {"form": form})
You now have a bound form to data from a specific reservation. I hope this helps. And more info on initial data here - initial form values
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].
I have this model that i want to make the create_by default for current user
class order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=200,default=None,null=True,blank=True)
in Django models I cant use request.user
I tried in views.py but i cant do it since i am using modelformsets
I tried this
def create_order(request):
orderformset = modelformset_factory(order, form=orderForm)
queryset = order.objects.none()
user = request.user
formset = orderformset(request.POST or None,queryset=queryset)
if formset.is_valid():
created=formset.save(commit=False)
created.User = request.user
created.save()
return redirect('home')
How i can give default user to this orders using modelformsets
In Your models file, use foreign key for user as follows
from django.contrib.auth import get_user_model
class order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
Then in your views wherever you handle GET request, intialize the form as such
def create_order(request, *args, **kwargs):
if request.method == 'GET':
initial = {'created_by':request.user}
form = orderform(initial=initial)
context['form'] = form
...
Then When you're handling the POST Request, just pass the POST and save it
def create_order(request, *args, **kwargs):
if request.method == 'POST':
form = orderform(request.POST)
if form.is_valid():
form.save()
...
This is How I'd handle This Scenario in django, Try out Class Based View. Its very good once you learn its abstract methods.
models.py
from django.contrib.auth import get_user_model
class Order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
forms.py
I Use hidden input to hide the input field, You can leave it open if you want. it will appear as a ChoiceField
from django import forms
from .models import Order
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = '__all__'
widgets = {
'created_by': forms.HiddenInput()
}
views.py
I use the Create view and override get initial method to pass user as initial value
from django.views.generic import CreateView
from .models import Order
class OrderCreateView(CreateView):
model = Order
form_class = OrderForm
template_name = 'order_create.html'
def get_initial(self):
initial = super().get_initial()
initial['created_by'] = self.request.user
return initial
order_create.html
...
<form method='POST'>
{{form}}
<button type="submit">Submit</button>
</form>
...
I'm learning in DJango and I have learned alot of stuff from the documentation and also in StackOverflow. Right now, I'm kinda stuck and I just want to know who can I check in a class based view, if the user is in the manager column in job model/ It can also be in the manager model that's fine too.
I tried using UserPassesTestMixinin order to check if user is part of it but I'm getting an error of Generic detail view createjob must be called with either an object pk or a slug in the URLconf.
I just need someone to point me to the right direction or give me a hint.I also tried, this:
class createjob (LoginRequiredMixin,CreateView):
model = Job
fields = ['member','title', 'description', 'file']
def form_valid(self,form):
form.instance.manager=self.request.user
return super().form_valid(form)
But it's giving me an error of Cannot assign "<SimpleLazyObject: <User: edlabra>>": "Job.manager" must be a "Manager" instance.
Here's my views.py:
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import ListView, CreateView
from .models import Job, Member
from profiles.models import User
from django.contrib.auth.decorators import login_required
# Create your views here.
class jobs(LoginRequiredMixin,ListView):
model = Job
template_name = 'users/user_jobs.html'
context_object_name = 'jobs'
def get_queryset(self):
return Job.objects.filter(member__member=self.request.user)
class createdjobs(LoginRequiredMixin,ListView):
model = Job
template_name = 'users/manager_jobs.html'
context_object_name = 'jobs'
def get_queryset(self):
return Job.objects.filter(manager__manager=self.request.user)
class teamview(LoginRequiredMixin,ListView):
model = Member
template_name = 'users/manage_team.html'
context_object_name = 'members'
def get_queryset(self):
return Member.objects.filter(manager__manager=self.request.user)
class createjob (LoginRequiredMixin,UserPassesTestMixin,CreateView):
model = Job
fields = ['member','title', 'description', 'file']
def test_func(self):
job=self.get_object()
if self.request.user == Job.manager:
return True
return False
Models.py:
from django.db import models
from profiles.models import User
# Create your models here.
class Points (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
points = models.IntegerField(default=0, null=False)
def __str__(self):
return self.user.username
class Profile (models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username}Profile'
class Manager (models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
manager = models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Member (models.Model):
name = models.CharField(max_length=30, blank=True, null=True)
manager = models.ForeignKey(Manager, on_delete=models.CASCADE)
member = models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Job (models.Model):
manager = models.ForeignKey(Manager, on_delete=models.CASCADE)
member = models.ForeignKey(Member, on_delete=models.CASCADE)
title = models.CharField(max_length=30, blank=False, null=False)
description = models.TextField()
datePosted = models.DateTimeField (auto_now = True)
file = models.FileField(null=True, blank=True,upload_to='job_files')
def __str__(self):
return self.title
assign user from manager table.
def form_valid(self,form):
form.instance.manager=Manager.objects.get(manager=self.request.user)
return super().form_valid(form)
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())