I'm fairly new to Django and am working on making user profile pages accessible by using the user's username in the url, e.g. mysite.com/profile/someusername
I'll be having links to the profile in a couple places, but the first place I'm experimenting on is in my navbar to access the logged-in user's page.
base.html
<a class="dropdown-item" href="{% url 'fillups:user_profile' username=user.username %}" class="btn btn-simple">Overview</a>
This correctly displays the currently logged-in user's name, for the case of this example we'll user the username seconduser
This is the url pattern I'm using for this:
path('profile/<str:username>/',views.UserProfile.as_view(),name='user_profile')
So far, the navbar will display the username, seconduser, and when I click the button I'm brought to the url /profile/seconduser/, which is what I want.
The problem is, I'm not able to now use the username in my view to query the objects for the given user. Here is what I have for this view so far
views.py
class UserProfile(TemplateView):
template_name = 'fillups/user_profile.html'
slug_field = "username"
slug_url_kwarg = "username"
def get_context_data(self, **kwargs):
context = super(UserProfile, self).get_context_data(**kwargs)
usr = get_object_or_404(User, username=self.kwargs.get("username"))
overview_stats = {
'total_cars': Car.objects.filter(username=usr).count(),
'total_fillups': Fillup.objects.filter(username=self.request.user).count(),
'total_distance': Fillup.objects.filter(username=self.request.user).aggregate(Sum('trip_distance')),
'total_gallons': Fillup.objects.filter(username=self.request.user).aggregate(total_gallons = Round(Sum('gallons'),4)),
'avg_price': Fillup.objects.filter(username=self.request.user).aggregate(avg_price = Round(Avg('price_per_gallon'),3)),
'total_spent': sum_total_sale(Fillup.objects.filter(username=self.request.user)),
'avg_mpg': avg_mpg(Fillup.objects.filter(username=self.request.user))
}
context['stats'] = overview_stats
context['active_cars'] = Car.objects.filter(status='Active').filter(username=self.request.user)
context['last_10_fillups'] = Fillup.objects.filter(username=self.request.user).order_by('-date')[:10]
return context
For now, everything in the overview_stats dict is what I originally had when I was just querying stuff for the logged-in user, where there was just a simple "myprofile" url. The problem I'm having her is that the get_object_or_404 isn't finding the user. I know that username=self.kwargs.get("username") is getting 'seconduser' like it should be, but for some reason I just can't get the user.
For some extra info, here is one of my models:
class Car(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=25)
make = models.CharField(max_length=25)
model = models.CharField(max_length=25)
model_year = models.IntegerField(choices=MODEL_YEARS)
status = models.CharField(max_length=10,choices=STATUS,default='Active')
def __str__(self):
return self.name
And in the initial Django tutorial I did, the instructor said it is best to extend the user model so it's easier to make changes, so I have this in a separate app, accounts/models.py
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return "#{}".format(self.username)
I've tried using the method in this question which is why I have the slug field stuff in my view currently, and while my question is essentially a duplicate of this question
I've been stuck on this all night and would really appreciate any help, thanks!
Remove the the self from self.kwargs.get("username"). It should be kwargs.get("username").
kwargs is an argument not on object property.
Related
Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. Users have to choose from the existing groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
What I want When Users come on the home page they see posts that were added since the last time they logged in
My Models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
My Views
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
context['object_list'] = Group.objects.filter(members=self.request.user)
#What am I doing wrong in the below code
new_posts = Post.objects.filter(created_at__gt=self.request.user.last_login).count()
context['new_posts'] = new_posts
else:
context['object_list'] = Group.objects.all()
return context
In my templates I have
<div class="list-group">
{% for group in object_list %}
{% if not new_posts %}
{{group.post.count}}
{% else %}
{{new_posts}}
{% endif %}
{% endfor %}
</div>
The Issue: Only the users who are already signed in and refresh their page see the new posts example:4 new, 3new etc... If a user signs in fresh after a new posts are created in a group. He does not see the 4 new, 3new . Instead it shows him just the number of posts in the group. not the new posts since he logged in. Why is this happening?
Well, its normal behavior because last_login stores datetime at which the user last logged in. When the user fresh logs in into the system, it stores the current time (timezone.now). So if a post is created before he is logged in, then it will not appear into the new_posts. So if you want to store the previous login/logout time, then you need to store it somewhere else(or make a new model field). You can try like this using user logged out signal:
from django.contrib.auth.signals import user_logged_out
def do_stuff(sender, user, request, **kwargs):
user.logout_time = timezone.now()
user.save()
user_logged_out.connect(do_stuff) # Hook an a method to user_logged_out signal to update logout time
And use it in the View like this:
last_post = Post.objects.last()
if last_post.created_at < request.user.last_login:
new_posts = Post.objects.filter(created_at__gt=self.request.user.logout_time).count()
context['new_posts'] = new_posts
Update
In this example, it is required to have a field to store logout time. If you have a CustomUser then you can directly create it in the CustomUser Model. Else, you can add it like this:
class Profile(models.Model):
user = models.OneToOneField(User)
logout_time = models.DateTimeField(default=timezone.now)
and store logout time like this:
user.profile.logout_time = timezone.now()
and filter New Posts Like this:
new_posts = Post.objects.filter(created_at__gt=self.request.user.profile.logout_time)
I am trying to extend the user model using a one to one relationship to a UserProfile model. I added some boolean fields and in the view I am trying to use those fields as permissions.
Here is my model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
FirstName = models.CharField(max_length=25)
LastName = models.CharField(max_length=25)
ProximityAccess = models.BooleanField(default=True)
NewProxAccess = models.BooleanField(default=False)
def __unicode__(self):
return self.user.username
and here is the view I am trying to use:
#login_required
def NewProx(request):
if UserProfile.NewProxAccess:
if request.method == 'POST':
form = ProxForm(request.POST)
if form.is_valid():
ProxPart_instance = form.save(commit=True)
ProxPart_instance.save()
return HttpResponseRedirect('/proximity')
else:
form = ProxForm()
return render(request, 'app/NewProx.html', {'form': form})
else:
raise PermissionDenied
I don't get any error messages but it does not work as intended. I was hoping that if the user profile had NewProxAccess set to False it would raise the PermissionDenied but it doesn't. I have the admin module wired up and I can select or deselect the checkbox for that field but it has no effect. If I comment out the rest I can get it to show the Permission Denied error so it has to be in the view (I think). I think I am missing a line the establishes the logged in user as the user instance so we can check to see if the user has the permission or not. I know there are a ton of ways to do this and there is probably a better way but for the sake of learning, what is it that I am missing for this to work?
Thanks
Scott
As you want to check access for particular profile but not UserProfile model you need to do:
if request.user.userprofile.NewProxAccess:
# your code
As a note: according to PEP8 best practices you should use camelCase only for naming Classes. For attrs, functions use underscore: my_function
Am using django-permission on simple test app (almost identical to the example used in the docs) to try to figure out how it works. I have read the documentation and tried to use the example app provided on this link.
The issue is when the author of an article is not able to edit/ delete the article.
The user in question has been granted all permissions in the admin section.
Key code listed below - any help much appreciated
test_app/models.py
class Article(models.Model):
created_by = models.ForeignKey(User)
created = models.DateField(auto_now_add=True)
modified = models.DateField(auto_now=True)
title = models.CharField(max_length=100)
content = models.TextField()
class Meta:
app_label = 'test_app'
from permission import add_permission_logic
from permission.logics import AuthorPermissionLogic
add_permission_logic(Article, AuthorPermissionLogic(
field_name='created_by',
any_permission = False,
change_permission = True,
delete_permission = True,
))
test_app/views.py
#permission_required('change_article')
def change_article(request, *args, **kwargs):
pk = kwargs.pop('pk')
template = 'test_app/edit.html'
article = models.Article.objects.get(id=pk)
if request.method == 'POST':
form = forms.Article_form(request.POST, instance=article)
if form.is_valid():
article = form.save(commit=False)
article.created_by = request.user
article.title = form.cleaned_data['title']
article.content = form.cleaned_data['content']
article.save()
return HttpResponseRedirect('/test/')
else:
raise Http404
else:
form = forms.Article_form(instance=article)
return render(request, template_name=template, context={'form':form})
test_app/perms.py
PERMISSION_LOGICS = (
('test_app.Article', AuthorPermissionLogic()),
)
EDIT
In the end there is a longer discussion on the project Github page available on this link.
While the objective of the question was resolved - it turns out that the function itself is a bit of a legacy function that is prone to unexpected behavior. The advice of the project owner is to use class based views rather than function based views.
I don't really get what
The user in question has been granted all permissions in the admin section.
means (not sure what "admin section" is) but
You don't need perms.py while you already add a permission logic in your models.py.
You need to use test_app.change_article instead (<app_label>.<perm>_<model_name>)
By the way, while you don't need perms.py so it's not a matter but the instance of AuthorPermissionLogic in perms.py is not properly configured while you haven't specified field_name there (the default value of field_name is 'author' if you don't specified.) https://github.com/lambdalisue/django-permission/blob/master/src/permission/conf.py#L24
I have a site that when a user clicks a bootstrap glyphicon link they should be redirected to another page, this page is of the same glyphicon but in a green color to make it seem as if by pressing the link they activated the button. During this trasition I want the field active on my Profile to go from False to True. I have the following code:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
ROLE = (
('CUSTOMER', 'User'), # (value to be set on model, human readable value)
('WORKER', 'Worker'),
)
role = models.CharField(max_length = 20, choices = ROLE, default = 'USER')
active = models.BooleanField(default = False)
views.py
def active(request):
request.user.profile.active = True;
return render(request, 'core/customer_active.html', {'user': request.user})
home.html:
<a href="{% url 'active' %}"><span class="glyphicon glyphicon-ok-sign" aria-hidden="true"></span></href>
I am not sure why request.user.profile.active = True; does not update the state of the field, what will?
As others have said, you need to save. However, it is the profile you need to save, not the user, since that is a separate model.
profile = request.user.profile
profile.active = True
profile.save()
Is this a permanent change to the "active" property? You need to save the user object, if so. Like this:
def active(request):
request.user.profile.active = True;
request.user.save()
return render(request, 'core/customer_active.html', {'user': request.user})
Edit: might be worth noting that this isn't the smartest way to update a user's profile, by saving this attribute every time they hit this view, but if you're just wondering why the True value isn't persisting, this is the reason why.
So, I am having some difficulty trying to slugify a title field in my model and still have it return the proper information.
Currently, a user can follow the url, if the list in their account exists under this regular expression:
url(r'^user/(?P<username>\w+)/list/(?P<listname>\w+)/$', mylistpage, name='lists'),
The issue I face is that the user can have a list containing spaces, but the regex bases their url off their list name. I am wanting to implement a slug url, but still have it retrieve the correct model/object information.
I am trying to have a slug field and then pre-populate it based on the list name, but I am lost at how this implementation is supposed to work. Much appreciation in advance from any insight.
Model
class newlist(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100,)
picture = models.ImageField(upload_to='profiles/', default = "/media/profiles/default.jpg")
slugurl = models.SlugField(default = slugurl(self))
def __str__(self):
return self.list_name
def slugurl(self):
return slugify(self.list_name)
Views
def mylistpage(request, username, listname):
context = RequestContext(request)
#make sure that the user is authenticated
if username == request.user.username:
#If the user is authenticated, then perform the following functions to the page
if request.user.is_authenticated():
#Store the current user request object into a variable
user = User.objects.get(username=username)
#Store the list name to the item that starts with the url input
listname = request.user.newlist_set.filter(list_name__iexact=listname)
listitems = request.user.newlist_set.all()
if not listname:
return redirect('/notfound')
else:
return redirect('/notfound')
return render_to_response('listview.html', {'lista': listname}, context)
I have used django-autoslug to great success. You can find a live example here.
SlugField is just a char field with a little syntactic sugar.
You will want to name your slug just slug so django can find it automatically in the URL resolution and passes the right parameter to views.
Your amended code would look like:
from autoslug import AutoSlugField
from django.db import models
class Newlist(models.Model): # Classes start with uppercase names by default
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100,)
picture = models.ImageField(upload_to='profiles/', default = "/media/profiles/default.jpg")
slug = AutoSlugField(populate_from='list_name')
def __str__(self):
return self.list_name
Your View:
def mylistpage(request,username, slug):
context = RequestContext(request)
#make sure that the user is authenticated
if username == request.user.username:
#If the user is authenticated, then perform the following functions to the page
if request.user.is_authenticated():
#Store the current user request object into a variable
user = User.objects.get(username=username)
#Store the list name to the item that starts with the url input
listname = request.user.newlist_set.filter(slug=slug)
listitems = request.user.newlist_set.all()
if not listname:
return redirect('/notfound')
else:
return redirect('/notfound')
return render_to_response('listview.html', {'lista': listname}, context)
urls.py
url(r'^user/(?P<username>\w+)/list/(?P<slug>[\w-]+)/$', mylistpage, name='lists'),