How to use <username> in detailview for django 2.0 - python

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=40, blank=True)
bio = models.TextField(max_length=500, null=True, blank=True)
def get_absolute_url(self):
return reverse('userprofile:to_profile', args=[str(self.user.username)])
So I have made this model to save it as whatever.com/userprofile:to_profile/thisisusername
And on url, I have
path('accounts/profile/<username>/', ProfileDetailView.as_view(), name="to_profile"),
For the view, I have
class ProfileDetailView(DetailView):
model = Profile
template_name = "account/profile.html"
I do know that default DetailView takes pk and slug but whenever I try to pass the username with
pk_url_kwarg = "username"
I get an error
invalid literal for int() with base 10
Is there anyway we can use username for generic detailview? or is pk the only way to go?

Two things you will need to do:
First change your URL's to:
path('accounts/profile/<str:slug>/', ProfileDetailView.as_view(), name="to_profile"),
Second define the method to get the SlugField
class ProfileDetailView(DetailView):
model = Profile
template_name = "account/profile.html"
def get_slug_field(self):
"""Get the name of a slug field to be used to look up by slug."""
return 'user__username'

You can’t use pk_url_kwarg because username is not the primary key of the Profile model that you are displaying in the view.
Override get_object instead.
from django.shortcuts import get_object_or_404
def get_object(self):
return get_object_or_404(Profile, user__username=self.kwargs['username'])

Related

How to obtain a user instance in django application

I'm trying to obtain a user instance for the profile page in django app but I'm finding some difficulties implementing that functionality. I have the following blocks of code:
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True, on_delete=models.CASCADE, default="")
image = models.ImageField(upload_to="images/user_profile_pics/", default="images/default_profile_pics/default.jpg")
firstname = models.CharField(max_length=50)
lastname = models.CharField(max_length=50)
def __str__(self):
return f'{self.lastname} profile'
serializers.py
class user_profile_serializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
views.py
class user_profile(generics.GenericAPIView):
serializer_class = user_profile_serializer
def get(self, request, *args, **kwargs):
user = self.get_serializer(request.user).data
if request.user.is_authenticated:
return Response(user, status=status.HTTP_200_OK)
else:
pass
urls.py
urlpatterns = [
path('profile/', user_profile.as_view(), name="user-profile"),
]
When ever I assess the profile url, I get an error message 'AnonymousUser' object has no attribute 'data' I have tried a couple of approaches but none worked. Please, how do I obtain a specific user from the database?
request.user is AnonymousUser when the user is not logged in. In that case that object does not have data attribute. Hence the error you get. One thing you can do is check request.user.is_authenticated and if the user is not authenticated, return some other value / or None. And try logging in before trying to access the user.data value.

Django, link a model field with an admin model field with ForeignKey

I'm trying to link a "normal" model field with an admin model field, for example I have a table "Post" and I want to add the admin username as a ForeignKey to the field "Author" of the table Post.
I mean :
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
username = models.ForeignKey(admin.username, on_delete=models.CASCADE)
Where admin.username refers the username of auth_user admin model
Thanks for your help
As the referencing the user model section of the documentation says, you can make use of settings.AUTH_USER_MODEL to obtain a reference to the user model that is used. You can use the to_field=… [Django-doc] to specify to what field of the model it should refer, so:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
to_field='username'
on_delete=models.CASCADE,
editable=False
)
By specifying editable=False [Django-doc] it will not automatically show up in ModelForms.
In views, you can then set the logged in user as author by specifing the author attribute. For example:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def some_view(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('name-of-some-view')
else:
form = PostForm()
return render(request, 'some_template.html', {'form': form})
Note: A ForeignKey does not store the string representation (or name) of the
referenced object in the column, it stores the primary key of the record it
references in a column with an _id suffix to a ForeignKey field. Therefore
ForeignKeys usually do not end with a _name suffix. You might want to
consider renaming the username field to author.
How about something like this?
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
user = models.ForeignKey(auth_user, on_delete=models.CASCADE)
#property
def username(self): return self.user.username
Usage:
some_post = Post.objects.get(id='the_post_id')
print(some_post.username) # prints some_post.user.username

Django Form: Only show manytomany objects from logged in user

The current problem is that my form shows the logged in user all Portfolios ever created. The form should only show portfolios that the logged-in user created.
Something like this:
associated_portfolios manytomany field = ...objects.filter(user=user_id)
I'm not sure if this should be implemented in the forms.py or views.py and if so how. I've been going through the django documentation and found 'formfield_for_manytomany' but not sure if this is only meant for admin.
Models.py
class Portfolio(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
description = models.CharField(max_length=250, blank=True, null=True)
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
body = RichTextUploadingField(blank=True, null=True)
associated_portfolios = models.ManyToManyField(Portfolio, blank=True)
created_on = models.DateField(auto_now_add=True, editable=False)
Views.py
class PostCreate(CreateView):
model = Post
form_class = PostCreateForm
def formfield_for_manytomany(self, db_field, request, **kwargs):
self.fields['associated_portfolios'] = Portfolio.objects.filter(user=self.request.user)
return super().formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
forms.py
class PortfolioCreateForm(ModelForm):
class Meta:
model = Portfolio
fields = ['user', 'name', 'description']
class PostCreateForm(ModelForm):
class Meta:
model = Post
fields = ['user', 'title', 'body', 'category', 'associated_portfolios']
Since you're using a ModelForm, the associated_protfolios field will be a ModelMultipleChoiceField [docs]. This field has a queryset attribute [docs]. We want to modify that attribute.
Django's CreateView has a method get_form, which in this case will grab your PostCreateForm. This is a good spot to filter the field's queryset, since we have access to the user:
class PostCreate(CreateView):
model = Post
form_class = PostCreateForm
def get_form(self, *args, **kwargs):
form = super().get_form(*args, **kwargs) # Get the form as usual
user = self.request.user
form.fileds['associated_portfolios'].queryset = Portfolio.objects.filter(user=user)
return form
Did you try this
self.fields['associated_portfolios'] = Post.objects.filter(associated_portfolios__portfolio__user=request.user)
OR
user_posts = Post.objects.filter(user=request.user)
self.fields['associated_portfolios'] = user_posts.associated_portfolios.all()
read more about M2M relationships querying here, because I think your problem may be with it.
Also, I'm not sure about your actual data maybe it's right and it gives a correct result as filtering Portfolio model against current user to get its objects looks right for me, but anyway double check everything again.
And as a final note, add related_name to your model fields so you can use it easily for reverse relations rather than going with Django's default naming, it will be clearer and give a better understanding.

How can I validate my forms or views in Django so that they can only edit the User Model only to those that belong to that data?

I have 2 models that I will allow users to edit separately, one is called User(Django default auth) and the other is UserProfile.
models.py (UserProfile)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatar', default='avatar/default.png')
header = models.ImageField(upload_to='header', default='header/default.png')
bio = models.TextField(max_length=140, blank=True)
website = models.URLField(max_length=200, blank=True)
location = models.CharField(max_length=30, blank=True)
date_birth = models.DateField(null=True, blank=True)
views.py
class UserUpdateView(generic.UpdateView):
"""
This view is for editing only the User model. /edit/
"""
model = User
slug_field = 'username'
form_class = UserForm
template_name = 'user/user_edit.html'
First, use the LoginRequiredMixin mixin so that only logged-in users can access the view.
Then, override the get_object method, and return the model instance you want to edit.
You don't need the username in the URL any more, so you can remove slug_field = 'username'.
from django.contrib.auth.mixins import LoginRequiredMixin
class UserUpdateView(LoginRequiredMixin, generic.UpdateView):
model = User
form_class = UserForm
template_name = 'user/user_edit.html'
def get_object(self):
return self.request.user
If you have a similar view for editing the user profile you would return self.request.user.userprofile instead.

Method in model that filter by user

My model:
class Document(models.Model):
title = models.CharField(_('title'), null=False, blank=False, max_length=250)
description = models.TextField(_('description'), null=True, blank=True)
is_favourite = my_method()
class FavouriteDocumentUser(models.Model):
document = models.ForeignKey(Document)
user = models.ForeignKey(CustomUser)
class Meta:
unique_together = ('document', 'user',)
I need a field 'is_favourite' that is true if exist in FavouriteDocumentUser a row with the id of the document and the id of logged user.
So the problem is: how can I get the current user in a method of the model?
I'm using these models into django rest framework.
You can't, because Django ORM is not aware of Django authentication. The current user is available in the request object, which is only available to your views. You need to pass your request.user to your model method. For example:
class Document(models.Model):
...
def is_favourite(user):
return self.favoritedocumentuser_set().filter(user=user).exists()
then you call this method from your views:
def my_view(request):
...
if mydocument.is_favorite(request.user):
...do something...

Categories