models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=500, verbose_name='Title')
post = models.TextField(verbose_name='Post')
post_time = models.DateTimeField()
update_time = models.DateTimeField()
exists = models.BooleanField(default=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.id:
self.post_time = timezone.now()
# self.exists = True
self.update_time = timezone.now()
return super(Post, self).save(*args, **kwargs)
class PostEditHistory(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
body = models.TextField()
edit_time = models.DateTimeField()
views.py
#login_required
def edit_post(request, username, post_id):
other_user = User.objects.get(username=username)
post = Post.objects.get(user=other_user.pk, pk=post_id)
prev_post = post
form = EditPostForm(data=request.POST or None, instance=post)
if form.is_valid():
PostEditHistory.objects.create(
user=request.user,
post=prev_post,
title=prev_post.title,
body=prev_post.post,
edit_time=prev_post.update_time
)
return redirect('single_post', username=username, post_id=post_id)
form.save()
context = {
'form': form
}
return render(request, 'blog_post/edit_post.html', context)
Before saving edited post, I'm trying to save the original post to PostEditHistory model. Every time a post is edited, it performs the same operations. But the code save the edited post to both Post and PostEditHistory model, and the original one gets lost. Help me to solve the problem.
Thanks in advance.
You are leaving of your function before use form.save()
return redirect('single_post', username=username, post_id=post_id)
form.save()
Change the position
form.save()
return redirect('single_post', username=username, post_id=post_id)
Suggestion: You should remove logic from views and handle it only in your models using functions...
Obs.: Dont use straight .objects.get() because if this do not exist will break your page and throw error, use filter to get the values without breaking and check it
class PostHistory(models.Model):
...
#staticmethod
def add_history(user, prev_post):
PostEditHistory.objects.create(
user=user,
post=prev_post,
title=prev_post.title,
body=prev_post.post,
edit_time=prev_post.update_time
)
#login_required
def edit_post(request, username, post_id):
...
post = Post.objects.filter(user=other_user.pk, pk=post_id).first()
prev_post = post if post else None
if form.is_valid():
PostEditHistory.add_history(request.user, prev_post)
form.save()
return redirect('single_post', username=username, post_id=post_id)
Related
My project is a discussion forum using Django and here are my create and update functions. The method of update_post should provide update functionality but every time I try to update a post it adds a new post. How can I update a resource?
#login_required
def create_post(request):
context = {}
form = PostForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
print("\n\n its valid")
author = Author.objects.get(user=request.user)
new_post = form.save(commit=False)
new_post.user = author
new_post.save()
form.save_m2m()
return redirect("home")
context.update({
"form": form,
"title": "Create New Post"
})
return render(request, "create_post.html", context)
#login_required
def update_post(request):
context = {}
author = Author.objects.get(user=request.user)
form = PostForm(request.POST , instance=author)
if request.method == "POST":
if form.is_valid():
print("\n\n its valid")
new_post = form.save(commit=False)
# new_post.user = author
new_post.save()
form.save_m2m()
return redirect("home")
context.update({
"form": form,
"title": "UpdatePost",
})
return render(request, "update_post.html", context)
In model total there are 4 classes Post , comment , reply and category and this is Post -
class Post(models.Model):
title = models.CharField(max_length=400)
slug = models.SlugField(max_length=400, unique=True, blank=True)
user = models.ForeignKey(Author, on_delete=models.CASCADE)
content = HTMLField()
categories = models.ManyToManyField(Category)
date = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=True)
hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk',
related_query_name='hit_count_generic_relation'
)
tags = TaggableManager()
comments = models.ManyToManyField(Comment, blank=True)
closed = models.BooleanField(default=False)
state = models.CharField(max_length=40, default="zero")
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
def get_url(self):
return reverse("detail", kwargs={
"slug":self.slug
})
#property
def num_comments(self):
return self.comments.count()
#property
def last_reply(self):
return self.comments.latest("date")
form = PostForm(request.POST , instance=author)
instance feels like it should be passed an instance of Post instead of an instance of Author. I assume that PostForm is a pretty standard ModelForm with model = Post in the Meta.
As a newbie in Django, I'm sure there is something obvious I'm not seeing. I have a user model with a one to one relationship to a userprofile model, where I'm storing the profile photo. I mixed DetailView and Formview because I want the user to go to his details page and update just the photo, but somehow its not working for me. I know I could do the job with UpdateView, but for didactic purposes, can anyone tell me why this is not working? I'm trying to updated the model fields in the form_valid method but this is not working, they just remain with the old values. I thought at the beginning it was the photo that could not be updated because of some errors on my side, but I've tried also updating other string fields and it doesnt work. Here the code: (the commented out fields are the places where I tried updating several model fields using get_object_or_404 and other functions)
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
form.save()
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
a = get_object_or_404(User, pk=self.get_object().id).userprofile
form = forms.UserPhotoForm(request.FILES['avatar'], instance=a)
# get_object_or_404(User, pk=self.get_object().id).apellido = '1234'
if form.is_valid():
# print(get_object_or_404(User, pk=self.get_object().id).userprofile.avatar)
# I tried updating several model fields here, but didnt work
# print(request.FILES['avatar'])
return self.form_valid(form)
else:
return self.form_invalid(form)
Here the model:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='profile_pics', default='profile_pics/default-user-icon-4.jpg', blank=True)
telefono = models.CharField(max_length=12, blank=True)
nombre = models.CharField(max_length=64, blank=True)
apellido = models.CharField(max_length=64, blank=True)
link = models.CharField(max_length=256, blank=True)
educacion = models.CharField(max_length=256, blank=True)
experiencia = models.TextField(max_length=512, blank=True)
birthdate = models.DateField(blank=True, null=True)
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
So I achieved it by using commit=False in my form_valid method:
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
user_instance = form.save(commit=False)
user_instance.avatar = form.cleaned_data['avatar']
user_instance.id = self.get_object().userprofile.id
user_instance.save(update_fields=['avatar'])
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
But now I have another problem. Every time I update the photo, a new photo is saved to the database. Is there a way of doing this and deleting the old photo? or replacing it?
I try to understand how I can fill automatically the seller user field:
Assume I got private group with two users (one is consumer the other is seller)
Only consumer can have access to this form.
So form.instance.consumer = self.request.user works well.
form.instance.channel = self.object works well also.
In my forms I'm using crispy forms and it's only display two fields (rate and comment).
But my question is how I can get seller user? I'm beginning with Django :)
#Channel REVIEW
#method_decorator(login_required(login_url='/cooker/login'),name="dispatch")
class ChannelReview(generic.DetailView, FormMixin):
model = Channel
context_object_name = 'channel'
template_name = 'channel_review.html'
form_class = ChannelRatingForm
def get_context_data(self, **kwargs):
context = super(ChannelReview, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def form_valid(self, form):
if form.is_valid():
form.instance.channel = self.object
form.instance.seller = seller #### Here is my question
form.instance.consumer = self.request.user
form.save()
return super(ChannelReview, self).form_valid(form)
else:
return super(ChannelReview, self).form_invalid(form)
def post(self,request,*args,**kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_valid(form)
def get_success_url(self):
return reverse('channel:channel_detail',kwargs={"slug":self.object.slug})
def get(self,request,*args,**kwargs):
self.object = self.get_object()
if not (request.user == self.object.consumer or request.user == self.object.seller):
return HttpResponseRedirect('/')
return super(ChannelReview, self).get(request,*args,**kwargs)
class Channel(models.Model):
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_consumer", blank=True, null=True)
name = models.CharField(max_length=10)
seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_seller")
class Rating(models.Model):
channel = models.OneToOneField(Channel,on_delete=models.CASCADE,related_name="rating_channel")
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_consumer')
seller = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_seller')
is_rated = models.BooleanField('Already rated', default=True)
comment = models.TextField(max_length=200)
publishing_date = models.DateTimeField(auto_now_add=True)
ratesugar = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
You can use the .update to update a model object, like this in case.
https://www.kite.com/python/examples/5166/django-models-update-a-model-object-without-loading-the-object-from-the-database
I am trying to give a user the option to change his/her first/last name through a ModelForm. When I press submit, I get hit with the UNIQUE constraint failed: auth_user.username error. Here are my codes:
students/forms.py:
class EditProfileForm(UserChangeForm):
def clean_password(self):
# Overriding the default method because I dont want user to change
# password
pass
class Meta:
model = User
fields = (
'first_name',
'last_name',
)
students/views.py:
User = get_user_model()
def student_profile_view(request, slug):
if request.method == 'GET':
# forms
edit_name_form = EditProfileForm(instance=request.user)
context = {
'edit_name_form': edit_name_form,
}
return render(request, "students/profile.html", context)
class ChangeNameView(SuccessMessageMixin, UpdateView):
template_name = 'students/edit_profile.html'
model = User
form_class = EditProfileForm
success_message = "Your name has been updated"
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.instance.student_profile = StudentProfile.objects.get(slug=request.user.student_profile.slug)
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
"""If the form is valid, save the associated model."""
form.instance.username = self.request.user
self.object = form.save(commit=False)
return super().form_valid(form)
def get_success_url(self):
return reverse('students:student_profile_view', kwargs={'slug': self.object.student_profile.slug})
also fyi, User model is foreign key with StudentProfile.
students/models.py:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='student_profile', on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
avatar = models.ImageField(upload_to='student_profile/', null=True, blank=True)
description = models.CharField(max_length=120, null=True, blank=True)
objects = models.Manager()
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("students:student_profile_view", kwargs={"slug": self.slug})
I am pretty new to class based view so maybe I'm doing something wrong there?
I assume you do not have the user within the form so you need the form
def get_context_data (self, *args, **kwargs)
ctx = super().get_context_data(*args, **kwargs)
if self.request.method == 'POST':
ctx['form'] = EditProfileForm(instance=self.request.user)
and remove def form_valid()
My form was working fine but suddenly it stops working and I'm stuck here, help me please!
When I prints form.errors in case of form not valid then it prints
user is a required field.
models.py
class TarWithDocker(models.Model):
name = models.CharField(max_length=255)
user = models.ForeignKey(User, related_name='deployments')
slug = AutoSlugField(populate_from='name', unique=True, name='slug')
archive = models.FileField(upload_to='archives', name='archive')
created_at = models.DateTimeField(default=timezone.now, editable=False)
class Meta:
ordering = ['-created_at']
views.py
class AwdDeployment(LoginRequiredMixin, CreateView):
template_name = 'deployments/awdDeployment.html'
def get(self, request, *args, **kwargs):
return render(request, 'deployments/awdDeployment.html', {})
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = AwdDeploymentForm(request.POST, request.FILES)
if form.is_valid():
deployment = TarWithDocker()
deployment.name = form.cleaned_data['name']
deployment.user = self.request.user
deployment.archive = form.cleaned_data['archive']
deployment.save()
return HttpResponse("Submitted")
else:
print("not saved")
else:
print("something happnes wrong")
form = AwdDeploymentForm()
return HttpResponseRedirect(reverse('users:deployments:awd'))
You have user in request, but may be not in post data
May be it help you:
post_data = request.POST.copy()
post_data.update({'user': request.user.pk})
form = AwdDeploymentForm(post_data, request.FILES)