i have created soft delete in my project and it is working fine. But the problem is that i am using User in-built model and OneToOneField with UserProfile. Now soft_delete function is in UserProfile Model where im using generic.DeleteView to delete user. The problem is that i could not pass object of User to UserProfile to set user_is_deleted to True. Here is my Code.
Views.py
class UserDeleteView(LoginRequiredMixin, generic.DeleteView):
model = User
template_name = 'users/user_confirm_delete.html'
success_url = '/users/'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.soft_delete()
return HttpResponseRedirect(self.get_success_url())
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_company = models.ForeignKey(Company, on_delete=models.CASCADE)
user_role = models.ForeignKey(Roles, on_delete=models.CASCADE)
user_is_deleted = models.BooleanField(default=False)
user_deleted_at = models.DateTimeField(blank=True, null=True)
def soft_delete(self):
self.user_is_deleted = True
self.user_deleted_at = timezone.now() - tdelta(days=-1)
self.save()
Soft Delete Reference - https://blog.khophi.co/soft-delete-django-quickly/
Change this
#classmethod
def soft_delete(self):
...
Change your delete() method as,
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.userprofile.soft_delete()
return HttpResponseRedirect(self.get_success_url())
Related
I'm learning the Class-Based View and ModelForm of Django, and I feel so confused with those things.
I want to create a page where users can post articles.
My implementation is as following:
models.py
class Post(models.Model):
id = models.CharField(primary_key=True, null=False, max_length=20)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=False)
content = models.TextField()
count_like = models.IntegerField(default=0)
created_time = models.DateTimeField()
last_modified = models.DateTimeField()
def save(self, *args, **kwargs):
''' On save, update last_modified '''
if not self.id:
count = Post.objects.count()
self.id = "PO" + str(count)
self.created_time = timezone.now()
self.last_modified = timezone.now()
return super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
print("pk"*100, self.pk)
return reverse('post_detail', kwargs={'pk': self.pk})
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['content']
def save(self):
return super().save(commit=False)
views.py
class PostCreateView(generic.CreateView):
model = Post
form_class = PostForm
template_name="post/create.jinja"
def form_valid(self, form):
self.object = form.save()
self.object.owner = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url())
urlpatterns
path('create/', PostCreateView.as_view(), name="post_create")
I checked and this works.
As you can see, the Post model has many attributes, but I just want users to fill 1 field content, the others would be automatically initiated. Is there any way to improve my implementation, because it's seperated into many places (model save() method, form save() method, valid_form() method).
One more question is what is self.object role? After assigned to a Post model instance, what would it be used for?
Please help me, if you don't understand what I say please ask in comment. Thanks ^^
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 created an update screen for django. However, although it redirects to successURL, the data has not been updated. I don't know why.
I need your help.
I will post it if necessary.
#view
class RecordDetailEdit(UpdateView,LoginRequiredMixin):
template_name = 'records/detail_edit.html'
model = URC
form_class = RecordDetailEditForm
pk_url_kwarg = 'id'
success_url = reverse_lazy('person:home')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
def get_form_kwargs(self):
kwargs = super(RecordDetailEdit, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#form
class RecordDetailEditForm(forms.ModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordDetailEditForm, self).__init__(*args, **kwargs)
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
#model
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
UET = models.DurationField(editable=True)
URT = models.DateTimeField(default=timezone.now,editable=True)
group = models.ForeignKey(group, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.UPRC
#url
path('<id>/edit/', views.RecordDetailEdit.as_view(), name='record_detail_edit'),
I changed it to the following.
def get_object(self, queryset=None):
obj = URC.objects.get(id=self.kwargs['id'])
return obj
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()
The problem is that I have a model secciones that is associated with a user and a productos model that is also associated with a user and model secciones,
class secciones(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(secciones, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
class productos(models.Model):
user = models.ForeignKey(User)
secciones = models.ForeignKey(secciones)
name = models.CharField(max_length=50)
image = models.ImageField(upload_to = 'productos')
precio = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
descripcion = models.TextField(max_length=300, null=True,blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(productos, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
I am creating a form so that the user can enter a new product in the productos model, but I just want to leave the sections of the model secciones of the user that I log in
This is the view
def agregar_producto(request):
if request.method == "POST":
modelform = AgregarProducto(request.POST,request.FILES)
print modelform
if modelform.is_valid():
modelform.save()
return redirect("/editar-perfil/")
else:
modelform = AgregarProducto()
return render(request, "home/AgregarProducto.html", {"form":modelform})
This is the form
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
How can I get the form to display only the sections of the model secciones of the user that logged in
Override the __init__ method of your model, and set the queryset for your secciones field.
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(AgregarProducto, self).__init__(*args, **kwargs)
self.fields['secciones'].queryset = secciones.objects.filter(user=user)
Now update your view so that you pass the user when instantiating the form. You need to do this for GET and POST requests.
if request.method == "POST":
modelform = AgregarProducto(request.POST, request.FILES, user=request.user)
...
else:
modelform = AgregarProducto(user=request.user)
You can use the login_required decorator, so that only logged in users can access the view.
from django.contrib.auth.decorators import login_required
#login_required
def agregar_producto(request):
Finally, please rename your models to Secciones and Productos. The lowercase secciones and productos look very unusual to most Django users, and can be confused with model instances.