add filed automatically to form in dajngo CreateView - django

I have Major model and Course model. when I add course to the course model using ModelForm and CreatView class, I want to add the field automatically.
I tried to use form_valid method but it get me this Error:
NOT NULL constraint failed: quizes_course.major_id
this is the major model:
class Major(models.Model):
name = models.CharField(max_length=50)
years = models.IntegerField(validators=[minMaxVal])
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("majors")
class Meta:
verbose_name_plural = "1. Majors"
and this is the course model:
class Course(models.Model):
major = models.ForeignKey(Major, on_delete=models.CASCADE)
year = models.IntegerField(validators=[minMaxVal])
name = models.CharField(max_length=100)
def __str__(self):
return f'{self.major.name}_{self.year}_{self.name}'
def get_absolute_url(self):
return reverse("courses", kwargs={"pk": self.major.pk})
class Meta:
verbose_name_plural = "2. Courses"
and this is the view:
class CreateCourse(CreateView):
model = Course
form_class = CourseCreateForm
template_name = 'quizes/create.html'
def form_valid(self, form):
form.save(commit=False)
major = get_object_or_404(Major, id=self.kwargs['pk'])
form.major = major.id
return super().form_valid(form)

I solve this problem by overriding post method in the CreateCourse:
def post(self, request, pk):
form = CourseCreateForm(request.POST)
if form.is_valid():
major = get_object_or_404(Major, id=pk)
form = form.cleaned_data
course = Course(major=major, name=form['name'], year=form['year'])
course.save()
return redirect(reverse('courses', kwargs={'pk':pk}))
return Http404('Error')

Related

How to assign model form field to a current logged in user in Django's class based views

I am trying to save a form with the current logged in user's username, but the error "Cannot assign "'Neshno_Games2'": "League.host" must be a "Manager" instance." occurs
Views.py
class CreateLeaguesView(generic.CreateView):
model = League
template_name = "leagues/create-league.html"
form_class = LeaguesCreationForm
success_url = "/leagues/leagues"
def get_context_data(self, **kwargs):
context = super().get_context_data( **kwargs)
context['leagues'] = League.objects.all()
return context
def form_valid(self, form):
manager = self.request.user.username
League.objects.create(
host = manager,
)
return super(CreateLeaguesView, self).form_valid(form)
Model.py
class League(models.Model):
name = models.CharField(max_length=30)
no_players = models.IntegerField(default=20)
start_date = models.DateField(blank=False, null=False)
end_date = models.DateField(blank=False, null=False)
prize = models.CharField(max_length=300)
host = models.ForeignKey(Manager, on_delete=models.CASCADE)
def __str__(self):
return self.name
forms.py
class LeaguesCreationForm(forms.ModelForm):
class Meta:
model = League
fields = (
"name",
"no_players",
"start_date",
"end_date",
"prize",
)
You can try like this:
class CreateLeaguesView(generic.CreateView):
model = League
def form_valid(self, form):
form.instance.host= self.request.user.manager # accessing one to one data
return super().form_valid(form)
More information can be found here in this documentation: https://docs.djangoproject.com/en/4.0/topics/db/examples/one_to_one/

how to stop create view if already created django

please help me in making this application in django project
Here is models.py file code
from django.db import models
from bio.models import Profile
class Question(models.Model):
name = models.TextField(max_length=500)
def __str__(self):
return self.name
class Answer(models.Model):
question = models.ForeignKey(Question, related_name='question', on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, related_name='profile', on_delete=models.CASCADE)
text = models.TextField(max_length=400)
created_on = models.DateTimeField(auto_now_add=True,null=True)
def __str__(self):
return "{0} on {1}".format(self.question, self.profile)
class Meta:
db_table = 'answer'
constraints = [
models.UniqueConstraint(fields=['profile', 'text', 'question'], name='unique answer')
]
here is views.py file code
class DetailViewMixin(object):
details_model = None
context_detail_object_name = None
def get_context_data(self, **kwargs):
context = super(DetailViewMixin, self).get_context_data(**kwargs)
context[self.context_detail_object_name] = self.get_detail_object()
return context
def get_detail_object(self):
return self.details_model._default_manager.get(pk=self.kwargs['pk'])
class AnswerCreate(DetailViewMixin, CreateView):
details_model = Question
context_detail_object_name = 'question'
model = Answer
form_class = NewAnswerForm
template_name = "qna/answer.html"
success_url = reverse_lazy('qna:list')
def form_valid(self, form):
form.instance.profile_id = self.kwargs.get('pk')
form.instance.question_id = self.kwargs.get('pk')
return super().form_valid(form)
here is my forms.py code
from django import forms
from .models import Answer
class NewAnswerForm(forms.ModelForm):
class Meta:
model = Answer
fields = ['text',]
def clean(self):
try:
Answer.objects.get(text=self.cleaned_data['text'].lower())
raise forms.ValidationError('Answer exists!')
except Answer.DoesNotExist:
pass
return self.cleaned_data
where am I going wrong????
I want that if user answers one question then he couldn't answer it again
how can i do form validation if object is already created
Add a UniqueConstraint to Answer so no combination of Question and Profile can be repeated:
https://docs.djangoproject.com/en/3.1/ref/models/constraints/#django.db.models.UniqueConstraint

How to reference a slug from a different model in get_success_url?

Setup
I have two models in my app. One is for a Journal and the other one is for entries to that journal. I wrote a CreateView class that will allow user to create a Journal entry for any Journal id currently located in. Ideally I want the class to "refresh" with the updated entry or in other words the get_success_url should lead the page we are currently located at.
views.py
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={'slug':self.object.slug})
def form_valid(self, form):
current_journal = to_journal.objects.get(journal_user=self.request.user, slug=self.kwargs['slug'])
form.instance.journal_user = self.request.user
form.instance.journal_name = current_journal
return super(ToJournalEntriesList, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(ToJournalEntriesList, self).get_context_data(**kwargs)
context['to_journal_entries'] = to_journal_entry.objects.all()
return context
models.py
class to_journal(models.Model):
journal_name = models.CharField(max_length = 40)
slug = AutoSlugField(populate_from='journal_name')
date_created = models.DateTimeField(auto_now_add=True)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_user) + " " + self.journal_name
def get_absolute_url(self):
return reverse('to-journals')
class to_journal_entry(models.Model):
body = models.TextField()
entry_date = models.DateTimeField(auto_now_add=True)
journal_name = models.ForeignKey(to_journal, on_delete=models.CASCADE,)
journal_user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
def __str__(self):
return str(self.journal_name) + " " + str(self.entry_date)
def get_absolute_url(self):
return reverse('to-journal-entries', args=(self.slug))
urls.py
urlpatterns = [
path('', CreateToJournal.as_view(), name='to-journals'),
path('<slug:slug>', ToJournalEntriesList.as_view(), name='to-journal-entries'),
]
Error
With the current setup that I have I get:
Which makes sense, because the to_journal_entry model does not have that that field.
Question
I am sure both my get_success_url() in views.py and get_absolute_url() in models.py are done incorrectly, but I could not find a good explanation of how those work. How should I set them up to achieve desired result? Thanks a ton in advance!
I appreciate everyone taking a look. Best, Rasul.
You can just follow the relationship:
def get_success_url(self):
return reverse('to-journals', kwargs={'slug': self.object.journal_name.slug})
Your self.object is a to_journal_entry. You probably want to use the to_journal, you can do that by obtaining the journal_name:
class ToJournalEntriesList(LoginRequiredMixin, CreateView):
model = to_journal_entry
template_name = 'to_journals/to_journal_entries_list.html'
fields = ('body',)
def get_success_url(self):
return reverse('to-journals', kwargs={ 'slug': self.object.journal_name.slug })
Note: usually the names of the models are written in PerlCase, so JournalEntry instead of to_journal_entry.

Voting on a model

I'm having trouble incrementing the vote_score attribute of my model every time it is voted on. This is my model:
# idea model
class Idea(models.Model):
User = ('accounts.User')
creator = models.ForeignKey(User, related_name='ideas', on_delete=models.PROTECT)
title = models.CharField(max_length=100, null=True, blank=True)
vote_score = models.BigIntegerField(default=0, null=True, blank=True)
votes = VotableManager()
#vote model
class Vote(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
create_at = models.DateTimeField(auto_now_add=True)
vote = models.NullBooleanField()
objects = VoteManager()
class Meta:
unique_together = ('user', 'content_type', 'object_id')
#classmethod
def votes_for(cls, model, instance=None):
ct = ContentType.objects.get_for_model(model)
kwargs = {
"content_type": ct
}
if instance is not None:
kwargs["object_id"] = instance.pk
return cls.objects.filter(**kwargs)
#manager
_VotableManager(models.Manager):
def __init__(self, through, model, instance, field_name='votes', extra_field='vote_score'):
self.through = through
self.model = model
self.instance = instance
self.field_name = field_name
self.extra_field = extra_field
self.name = model.title
#instance_required
def up(self, user, vote):
with transaction.atomic():
if self.through.objects.filter(user=user, content_object=self.instance).exists():
c_type = ContentType.objects.get_for_model(self.instance)
vote_obj = self.through.objects.get(user=user, object_id=self.instance.id, content_type=c_type)
vote_obj.vote = vote
vote_obj.save()
self.instance.save()
else:
self.through(user=user, content_object=self.instance, vote=vote).save()
if self.extra_field:
setattr(self.instance, self.extra_field, F(self.extra_field)+1)
self.instance.save()
My goal is to have it so when the idea is created the creator automatically counts as 1 vote toward it so that falls under this view:
# idea create view
class IdeaCreateView(LoginRequiredMixin, CreateView):
model = Idea
form_class = IdeaCreateForm
template_name = 'idea_create.html'
success_url = 'success'
def dispatch(self, *args, **kwargs):
self.user = get_object_or_404(User, pk=kwargs['pk'])
return super(IdeaCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
idea = form.save(commit=False)
idea.creator = self.request.user
idea.save()
idea.votes.up(user=idea.creator, vote=True)
idea.vote_score += 1
return HttpResponseRedirect('success')
And then to allow other users to vote on it as well, which falls under this view:
#vote view
class IdeaVoteView(LoginRequiredMixin, CreateView):
form_class = VoteForm
required_fields = ('action',)
template_name = 'vote_confirm.html'
success_url = 'success'
def form_valid(self, form):
obj = Idea.objects.get(pk=self.kwargs['pk'])
user = self.request.user
user_id = self.request.user.pk
object_id = obj.pk
content_type_id = 10
form_class = VoteForm
self.vote = form.save(commit=False)
self.vote.user_id = user_id
self.vote.object_id = object_id
self.vote.content_type_id = content_type_id
if obj.votes.exists(user_id):
return HttpResponseRedirect('failure')
else:
self.vote.save()
obj.votes.up(user_id)
return HttpResponseRedirect('success')
def get_object(self, queryset=None):
obj = Idea.objects.get(pk=self.kwargs['pk'])
voters = obj.get_voters()
return voters
Why doesn't setting the extra_field attribute on the manager take care of this and what's going wrong in the views?
Wow, great news... After talking to myself for hours I figured it out. I created this model function:
def vote_up(self):
self.vote_score += 1
self.save()
The problem was I wasn't calling the save() method on this function so the increment wasn't getting saved! Really simple, but I glossed right over it. Anyway, maybe answering the question will help someone.

Django: Change Choice selection from FK in a Form linked to an UpdateView

The Model flow Topic -> Section -> Article.
I am building an Update View for my FAQ project to update already created Articles. I want the Form to provide a selection of Sections based on the Topic the Article was created under. As I already have the Articles PK passed in through the URL I was hoping to use it to walk back up to the Topic when creating my filter. I am receiving an object has no attribute ‘section’ error when the template is attempting to render the form on line self.fields['section'].queryset = Section.objects.filter(topic_id=self.section.topic.id) in the UpdateAriticleForm. I need help to figure out my query filter.
The URL:
url(r'^ironfaq/article/update/(?P<pk>\d+)/$', ArticleUpdateView.as_view()),
The Form:
from django import forms
from .models import Topic, Section, Article
class CreateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Section.objects.none())
def __init__(self, *args, **kwargs):
topic_pk = kwargs.pop('topic_pk')
super(CreateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id=topic_pk)
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
class UpdateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Section.objects.none())
def __init__(self, *args, **kwargs):
super(UpdateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id=self.section.topic.id)
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
The View:
class ArticleUpdateView(UpdateView):
model = Article
form_class = UpdateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
article.activity_user = self.request.user.username
article.activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%d" % (article.section.topic.slug,article.section.slug,article.id)
return super(ArticleUpdateView,self).form_valid(form)
The Models:
class Topic(Audit):
name = models.CharField(max_length=255)
icon = models.CharField(max_length=25,blank=True,null=True)
sort = models.SmallIntegerField()
slug = models.SlugField()
class Meta:
verbose_name_plural = "topics"
def __str__(self):
return self.name
class Section(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "sections"
def __str__(self):
return self.name
class Article(Audit):
title = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
section = models.ForeignKey(Section,on_delete=models.CASCADE)
answer = models.TextField()
vote_up = models.IntegerField(default=0)
vote_down = models.IntegerField(default=0)
view_count = models.IntegerField(default=0)
class Meta:
verbose_name_plural = "articles"
def __str__(self):
return self.title
The answer to the this issue was not passing 'pk' as a argument to the form and to add get_form_kwargs to the view to enable the form to see the 'pk' passed in the URL.
Form:
class UpdateArticleForm(forms.ModelForm):
section = forms.ModelChoiceField(queryset=Article.objects.none())
def __init__(self, pk, *args, **kwargs):
super(UpdateArticleForm, self).__init__(*args, **kwargs)
self.fields['section'].queryset = Section.objects.filter(topic_id__exact=Article.objects.filter(id=pk).first().section.topic.id)
View:
class ArticleUpdateView(UpdateView):
model = Article
form_class = UpdateArticleForm
template_name = "faq/form_create.html"
def get_form_kwargs(self):
kwargs = super(ArticleUpdateView,self).get_form_kwargs()
kwargs.update(self.kwargs)
return kwargs
def form_valid(self, form):
article = form.save(commit=False)
article.activity_user = self.request.user.username
article.activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%d" % (article.section.topic.slug,article.section.slug,article.id)
return super(ArticleUpdateView,self).form_valid(form)