I have a model Creator which is linked to the Django standard User model via a OneToOneField field. The Creator model is there to allow me more flexibility when adding/editing information of a Creator. Now for deleting a Creator I use the generic DeleteView. However, this does not delete the User instance and hence is not completely what I am looking to do (e.g. the username of the deleted Creator is "already taken"). Is there a way to use the default DeleteView to let a user delete his account? What is the best practice to do a "delete account" operation regarding deleting the User instance, logging out the user, and confirming the success of the operation?
What I tried so far:
models.py
class Creator(models.Model):
first_name = models.CharField(max_length=100, null=True, blank=True)
last_name = models.CharField(max_length=100, null=True, blank=True)
street_and_number = models.CharField(max_length=200, null=True, blank=True)
zip_code = models.CharField(max_length=30, null=True, blank=True)
location = models.CharField(max_length=100, null=True, blank=True)
state_province = models.CharField(max_length=100, null=True, blank=True)
country = models.CharField(max_length=100, null=True, blank=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.user)
super(Creator, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('creator-detail', args=[str(self.slug)])
views.py
class CreatorDelete(LoginRequiredMixin, DeleteView):
#model = User # does not work
model = Creator
success_url = reverse_lazy('index')
# these two methods only give access to the users own profile but not the others
def user_passes_test(self, request):
if request.user.is_authenticated:
self.object = self.get_object()
return self.object.user == request.user
return False
def dispatch(self, request, *args, **kwargs):
if not self.user_passes_test(request):
return redirect_to_login(request.get_full_path())
return super(CreatorDelete, self).dispatch(request, *args, **kwargs)
urls.py
path('creator/<slug:slug>/delete/', views.CreatorDelete.as_view(), name='creator-delete')
Also this thread seemed quiet promising, but since I would like to use a user_confirm_delete.html template and rather a class based view (to implement a check that a user can only delete his own account) I didn't manage to make it work.
Override the delete() method as,
class CreatorDelete(LoginRequiredMixin, DeleteView):
.....
.....
.....
# your code
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.user.delete() # deleting the default "User" model
return HttpResponseRedirect(reverse('index'))
Related
I want to delete the data inside the file field and the file uploaded through the file field.
I'm beginner.
models.py
class Fruits(models.Model):
name = models.CharField(
_('Name'),
max_length=200,
)
description = models.TextField(
_('Description'),
null=True, blank=True,
)
this_is_my_file = models.FileField(
_('Photo image'),
null=True,
blank=True,
upload_to='myfile/'
)
def __str__(self):
return self.name
def delete(self, *args, **kwargs):
if self.this_is_my_file:
self.this_is_my_file.delete()
super().delete(*args, **kwargs)
Like the code above, I added 'def delete' to the existing model as shown above through search.
But I don't know how to use this function.
How do I use this in view and template?
My part of view is below.
views/fruits.py
class FruitsDetailView(LoginRequiredMixin, DetailView):
template_name = 'frutis/detail.html'
login_url = 'login'
model = MyObjects
def get_context_data(self, **kwargs):
pk = self.object.pk
context = super().get_context_data(**kwargs)
...
return context
How do I execute the delete function in this view?
I want to delete it when I press the 'delete' button on the template.
Should I send a post request from template and receive it from view?
I've been thinking about it for 4 days, but I don't know what to do.
I'm using Django and I'm getting the error AttributeError at /admin/network/post/
'Post' object has no attribute 'user'
The strange thing is this error happens when I'm looking at the admin section, and clicking 'Posts.' I only have models for users and posts. Not sure how to fix this error because so far I've never gotten an error like this when clicking it in the admin section of the site: http://127.0.0.1:8000/admin/
I think the issue is in my model because the view for creating a post works totally fine.
models.py
class User(AbstractUser):
pass
class Post(models.Model):
text = models.TextField(max_length=500, blank=True, null=True)
username = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='author',
null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(
User, blank=True, related_name="liked_user")
def __str__(self):
return self.user.username
class Follow(models.Model):
target = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='followers')
follower = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='targets')
views.py
def make_post(request):
if request.method == "GET":
form_for_post = {'form': PostForm()}
return render(request, "network/make_post.html", form_for_post)
else:
form = PostForm(request.POST)
if form.is_valid():
text = form.cleaned_data['text']
new_post = Post.objects.create(
text=text,
username=request.user,
)
return render(request, "network/make_post.html", {
"new_post": new_post,
})
You defined the field that refs to a User in the Post model to be username, not user, although user should be a better idea.
You thus should implement the __str__ method as:
class Post(models.Model):
# …
username = models.ForeignKey('User', on_delete=models.CASCADE, related_name='author', null=True, blank=True)
# …
def __str__(self):
return self.username.username
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
What a want to do: When a user is logged in, and he or she makes a post, the name of that user should automatically be assigned in my database posts.
What it's doing: It's not adding a user automatically, but i am able to assign a user manually, so I'm accessing the user database, and seeing whom i can attach to a newly made post.
My question is then, how can i get this process done automatically?
Here is my code from the model.py in the posts app:
from __future__ import unicode_literals
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class Post(models.Model):
title = models.CharField(max_length=120)
content = models.TextField()
#email = models.EmailField(null=True, blank=True, default=None)
user = models.ForeignKey(User, null=True,)
#upload = models.FileField(null=True, blank=True, default=None)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=True, auto_now_add=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:detail", kwargs={"id":self.id})
class Meta:
ordering = ["-timestamp", "-updated"]
I am getting the user class via User = settings.AUTH_USER_MODEL and the AUTH_USER_MODEL is referring in settings.py to a class called MyUser in another models.py who originates from an app called accounts.
here is the code from that class:
class MyUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
is_admin = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
USERNAME_FIELD = 'email'
Here is the code from views.py in the posts app:
def post_create(request):
form = PostForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
# Message succes
messages.success(request, "Succesfully Created ")
return HttpResponseRedirect(instance.get_absolute_url())
else:
messages.error(request, "Not Succesfully created")
context = {
'form': form,
}
return render(request, app_name+"/post_form.html", context)
Here is the forms.py in the posts app:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = {
"title",
"content",
"user",
#"email",
#"upload",
}
Here are two pictures to illustrate my problem:
The post create site
The django administration
Let me now if any more code is needed, appreciate any feedback as well.
I don't have a lot of rep on stack overflow so please let me know if this is poorly explained, and i shall re right it.
Simply change:
instance = form.save(commit=False)
instance.save()
to
instance = form.save(commit=False)
if request.user.is_authenticated():
instance.user = request.user
instance.save()
If user is logged in, i think the Combobox should not
appear, so you can do that on forms.py
forms.py
class PostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(PostForm, self).__init__(*args, **kwargs)
if not self.user.is_authenticated():
self.fields['user'] = forms.ModelChoiceField(
required=True,
queryset=User.objects.all())
class Meta:
model = Post
fields = {
"title",
"content",
# "user",
#"email",
#"upload",
}
on views.py
def post_create(request):
form = PostForm(request.POST or None, user = request.user)
if form.is_valid():
if request.user.is_authenticated():
form.instance.user = request.user
form.save()
...
return render(request, app_name+"/post_form.html", context)
If you want the Combobox has selected with the user logged in, you can pass initial data on views.py, like this:
def post_create(request):
if request.method == 'GET':
form = PostForm(initial = {'user' : request.user})
I am building a django based web app to index some online learning resources, such as MOOCS. Some MOOCS are 'open' meaning they can be started at any time, at the learners own pace, while others have a defined start and end date. It makes sense to me that if the course is open, start date should not be required, but if it is not open, I would like start date to be required. Is there ant way to do this in my model.py, or should it be handled solely with form validation. Thanks for the help.
Currently:
class Post(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=70)
description = models.CharField(max_length=300)
url = models.URLField()
post_date = models.DateTimeField('date posted')
start_date = models.DateTimeField('date course starts', null=True, blank=True)
open = models.BooleanField(default=False)
....
def __unicode__(self):
return self.title
You seem to know how to use form validation, so to answer your question:
What you are trying to do is best accomplished using form validation as you said. Now, if you want to do this on a model level, in a way that you would have integrity (not DB integrity, but logical integrity), you should override the model's clean method. You can do this via something along the lines of:
def clean(self, *args, **kwargs):
if not self.open and not self.start_date:
raise ValidationError('This course is not open, therefore you need a start date')
return super(Post, self).clean()
Now, if you want an added layer of logical integrity by also forcing clean() to run when you save() a model instance via command line, you can call self.clean() in the save() model method as well (note that this will run self.clean() twice in non-command line instances). Here is an example of what the final code can look like:
class Post(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=70)
description = models.CharField(max_length=300)
url = models.URLField()
post_date = models.DateTimeField('date posted')
start_date = models.DateTimeField('date course starts', null=True, blank=True)
open = models.BooleanField(default=False)
....
def clean(self, *args, **kwargs):
if not self.open and not self.start_date:
raise ValidationError('This course is not open, therefore you need a start date')
return super(Post, self).clean()
def save(self, *args, **kwargs):
self.clean()
return super(Post, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
I have pretty simple and obvious question that i am trying to search for a while now.
I have a model, Picture, that has foreign keys like created_by, Province, and City.
What I want is to get all model fields serialized to json.
Models.py:
class Picture(models.Model):
name = models.TextField("Title", max_length=10000, null=True, blank=True)
meta_data = models.TextField("meta_data", null=True, blank=True)
created_by = models.ForeignKey(User, related_name="created_by")
city = models.ForeignKey(City, null=True, blank=True)
pro = models.ForeignKey(Province, verbose_name="Province")
class Province(models.Model):
name = models.CharField(max_length=50)
pi_meta_data = models.TextField(null=True, blank=True)
intro = models.CharField(max_length=1000, null=True, blank=True)
description = models.TextField(max_length=10000, null=True, blank=True)
class City(models.Model):
name = models.CharField(max_length=50)
pi_meta_data = models.TextField(null=True, blank=True)
intro = models.CharField(max_length=1000, null=True, blank=True)
description = models.TextField(max_length=10000, null=True, blank=True)
You can use Django Rest Framework, and serialize models with ease.
Your model serializers would be:
class PicturesSerializer(serializers.ModelSerializer):
class Meta:
model = Picture
# Only including fields in this case to explicitly show that
# every field in the Pictures model is serialized. In the rest of
# the Serializes fields will be left out for brevity.
fields = ("name", "meta_data", "created_by", "city", "pro")
class ProvinceSerializer(serializers.ModelSerializer):
class Meta:
model = Province
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
Thats it. Seriously.
Everything is nicely packed into json for you, even if you change your models down the line.
But how easy is it to use the serialized data? A simple view using those serialized models would be:
class PicturesList(APIView):
"""
List all Pictures, or create a new Picture.
"""
def get(self, request, format=None):
pictures = Pictures.objects.all()
serializer = PicturesSerializer(pictures, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = PicturesSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The documentation is literally amazing. They have a cool tutorial to get you started. They even have a sweet browseable api that lets you visually navigate through your api.
There is a "Django Full Serialization" module which is part of wadofstuff which can serialize related objects among other things. This answer https://stackoverflow.com/a/3753769/187729 has more info.