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
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/
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
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.
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.
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)