I'm trying to do a system where an user gains points if he asks a question but the points field isn't increasing when a user does that.
my model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.PROTECT, related_name='post')
category = models.ForeignKey(Category, on_delete=models.PROTECT)
type = models.CharField(max_length=30, choices=TYPE, default='Question')
title = models.CharField(max_length=100, unique=True)
content = models.TextField()
views = models.IntegerField(default=0)
votes = models.ManyToManyField(User, blank=True, related_name='vote')
featured = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
my view:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
success_url = '/'
fields = ['title', 'content', 'category']
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.author.points + 15
return super().form_valid(form)
When I go to the current user in the admin page the value doesn't change.
You should add more clarity to your code, but as I can assume and as Thierno said you are not accessing the object and not saving it afterwards.
What you need to do is once you make your post request, --and since you need access to the user--, save your post and then do something like:
post_user = self.request.user
post_user.points +=15
post_user.save()
Related
I am trying to save an object to my database while adding it to the Many to Many field of another object. I already tried many other solutions from here but nothing worked so far.
Model:
class SellerPost(models.Model):
post_uuid = models.UUIDField(default=uuid.uuid4, editable=False)
seller = models.ForeignKey("User", on_delete=models.CASCADE)
text_content = models.TextField()
comments = models.ManyToManyField("SellerPostComment", blank=True)
class SellerPostComment(models.Model):
comment_id = models.IntegerField(primary_key=True)
post = models.ForeignKey(SellerPost, on_delete=models.CASCADE)
addressed = models.ForeignKey("User", on_delete=models.CASCADE, null=False, related_name="seller_addressed_comment")
commenter = models.ForeignKey("User", on_delete=models.CASCADE, null=False)
content = models.TextField()
View (i cut everything but the essential part that has sth to do with the error):
post = request.POST["post"]
post_obj = SellerPost.objects.get(post_uuid=post)
comment = comment_form.save(commit=False)
comment.addressed = user
comment.commenter = request.user
comment.post = post_obj
comment.save()
post_obj.comments.add(comment)
return redirect(index)
class PostCommentForm(forms.ModelForm):
class Meta:
model = SellerPostComment
fields = ("content",)
def save(self, commit=True):
comment = super(PostCommentForm, self).save(commit=False)
if commit:
comment.save()
return comment
Error:
Cannot add "<SellerPostComment: SellerPostComment object (None)>": the value for field "sellerpostcomment" is None
The form is valid but it just won't save the comment to the M2M field of the post.
Thanks in advance!
I am new to django and I created this "apply now form" exclusively for tutors that when they submit the form it will appear to the admin site, and I will manually check it if they are a valid tutor. And if they are a valid tutor, I will check the is_validated booleanfield in the admin site to the corresponding tutor that sent the form, so that he/she will have access to other things in the site. But I am having this problem that when you submit the form this comes up..
NOT NULL constraint failed: account_tutorvalidator.user_id
I have search for some solutions and also read similar questions here but I still couldn't understand what to do.. could someone help me out with this?
here is my models.py
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_tutor = models.BooleanField(default=False)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
phone_number = models.CharField(max_length=11, blank=False, null=True)
current_address = models.CharField(max_length=100, null=True)
image = models.ImageField(default='default-pic.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.first_name} {self.last_name}'
class TutorProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True,
related_name='tutor_profile')
bio = models.CharField(max_length=255, blank=True)
is_validated = models.BooleanField(default=False)
def __str__(self):
return f"{self.user.first_name} {self.user.last_name}'s Profile"
class TutorValidator(models.Model):
user = models.ForeignKey(TutorProfile, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
dbs = models.ImageField(upload_to='dbs_pics')
driving_license = models.ImageField(upload_to='drivers_license_pics', null=True, blank=True)
national_id = models.ImageField(upload_to='national_id_pics', null=True, blank=True)
def __str__(self):
return f"{self.first_name}'s application form"
my forms.py
class TutorValidationForm(forms.ModelForm):
class Meta:
model = TutorValidator
fields = ['first_name', 'last_name', 'driving_license', 'national_id']
labels = {
'national_id': _('National ID')
}
my views.py
class TutorValidatorView(LoginRequiredMixin, FormView):
template_name = 'account/tutor_validator.html'
form_class = TutorValidationForm
success_url = '/'
The error is because TutorValidator requires that you set the user profile foreign key which your form currently does not support, so you need a way to set this to the object you are creating, and use the current logged in user (the one who is submitting the form).
You can do this by overriding form_valid. Try with:
class TutorValidatorView(LoginRequiredMixin, FormView):
...
def form_valid(self, form):
tutor_validator = form.save(commit=False)
tutor_validator.user = self.request.user.tutor_profile
tutor_validator.save()
return HttpResponseRedirect(self.get_success_url())
Note that the current user needs to already have an existing TutorProfile. Otherwise you need to create that first to connect it to TutorValidator
I'm trying to update my an instance of Post model each time a view PostDetail is generated. So far I've tried multiple approaches but none of them worked. I know that there is ready solution (django-hitcounter) but I would like to write one myself so I can understand what is happening.
The goal there is to add 1 to post.views each time user accesses PostDetail view.
models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.IntegerField(default=0)
class Meta:
ordering = ['-created_on']
views.py
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
urls.py
urlpatterns = [
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
]
Once you've got to the point where Django can return a response (eg: it's found the Post object successfully etc...) - you can increment your view count for the object then and proceed to returning the response, so if you change your view to be:
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
def render_to_response(self, context, **kwargs):
self.object.views += 1
self.object.save()
return super().render_to_response(context, **kwargs)
I'm trying to build courses and add lessons to a course later and the problem I encounter is that every user can choose to add courses to another person created courses.
Like if you create some courses, another user will see as an option to add his lesson to it
views.py
def creatingLessonsForm(request):
form = CreatingLessonsForm(request.POST or None)
if form.is_valid():
post = form.save(commit=False)
post.CreatedBy = request.user
post.save()
form = CreatingLessonsForm()
context = {'form': form}
return render(request, 'courses/creatingLessonsForm.html', context)
models.py
class CreatingCourses(models.Model):
NameOfTheCourses = models.CharField("Name of the courses", max_length=60, blank=False)
Category = models.ForeignKey(Subject, on_delete=models.CASCADE)
CreatedBy = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
Document = models.ForeignKey(Document, on_delete=models.SET_NULL, verbose_name= "Select document for courses introduction", blank=True , null=True)
IncludeTest = models.ForeignKey(GenaratedTest, on_delete=models.SET_NULL, verbose_name= "Include test for courses", blank=True , null=True)
AdditionalInfo = models.TextField("Additional info for courses introduction", max_length=300, blank=False)
Note = models.TextField("Notes", max_length=180, blank=True)
Show_the_courses = models.BooleanField(verbose_name= "Show the courses for everyone?",default=True)
def __str__(self):
return str(self.NameOfTheCourses) if self.NameOfTheCourses else ''
class CreatingLessons(models.Model):
Courses = models.ForeignKey(CreatingCourses, on_delete=models.SET_NULL, null=True)
NameOfTheLesson = models.CharField(max_length=60, verbose_name= "Name of the lesson", blank=False)
Document = models.ForeignKey(Document, on_delete=models.SET_NULL, verbose_name= "Document for lesson", blank=True , null=True)
CreatedBy = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
Lesson = models.TextField(max_length=250, verbose_name= "Lesson", blank=False)
Note = models.TextField("Notes", max_length=100, blank=True)
Show_the_lesson = models.BooleanField(verbose_name= "Show the lesson inside courses?",default=True)
forms.py
class CreatingCoursesForm(forms.ModelForm):
class Meta:
model = CreatingCourses
fields = ['NameOfTheCourses', 'Category', 'IncludeTest', 'Document' , 'AdditionalInfo', 'Note', 'Show_the_courses' ]
class CreatingLessonsForm(forms.ModelForm):
class Meta:
model = CreatingLessons
fields = ['Courses', 'NameOfTheLesson', 'Document', 'Lesson', 'Note', 'Show_the_lesson']
Image of webpage:
You need to pass the user when you initialize the form and then filter the queryset for the available courses that can be selected
class CreatingLessonsForm(forms.ModelForm):
def __init__(self, data=None, user=None, **kwargs):
super().__init__(data, **kwargs)
self.fields['Courses'].queryset = CreatingCourses.objects.filter(CreatedBy=user)
And then when you initialize the form pass the user
# When rendering the initial form
form = CreatingLessonsForm(user=request.user)
# When passing POST data to the form
form = CreatingLessonsForm(request.POST, user=request.user)
One option would be to modify your to filter the courses by user.
class CreatingLessonsForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
request_user = kwargs.pop('request_user')
super().__init__(*args, **kwargs)
self.fields['Courses'].queryset = self.fields['Courses'].queryset.filter(
CreatedBy=request_user)
For that to work you will need to pass in the user of the request to the form, maybe like this:
def creatingLessonsForm(request):
data = request.POST.copy()
data['request_user'] = request.user
form = CreatingLessonsForm(data)
...
When a user creates a post using the CreateView I want it so when a user submits the Post, they then see the post they just made. But for some reason my get_absolute_url() is not working.
Prior to this I started to work on slugifying the Post and Category models and haven't been able to see if they work due to the fact get_absolute_url won't work.
Models:
class Category(models.Model):
title = models.CharField(max_length=200)
colorcode = models.CharField(max_length=20, blank=True, null=True)
description = models.TextField()
image = models.ImageField(blank=True, null=True)
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True)
image = models.ImageField(blank=True, null=True)
live = models.BooleanField(default=False)
slug = models.SlugField(unique=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Post.objects.filter(slug=slug).order_by("-pk")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().pk)
return create_slug(instance, new_slug=new_slug)
return slug
def pre_save_post_reciever(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_reciever, sender=Post)
Views:
class IndexView(ListView):
model = Post
queryset = Post.objects.filter(live=True)
template_name = "public/index.html"
class PostEdit(object):
model = Post
fields = '__all__'
success_url = '/'
class PostCreateView(LoginRequiredMixin, PostEdit, CreateView):
fields = ['title', 'text', 'category', 'image']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, PostEdit, UpdateView):
fields = ['title', 'text', 'category', 'image']
Anyone has any idea on how to fix this?
If behaves according to this definition:
class PostEdit(object):
model = Post
fields = '__all__'
success_url = '/' # <<<---
In your case success_url is not static and cannot be defined as attribute. You have to override get_success_url instead like that:
def get_success_url(self):
return self.get_object().get_absolute_url()
also have a look at this answer.
upd
If the redirect is that simple then, as Daniel Roseman mentioned, you don't need to specify success_url at all - what you want is the default behavior.
You don’t even need to provide a success_url for CreateView or UpdateView - they will use get_absolute_url() on the model object if available.