Setting a field on a model not using a form - django

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.

Related

No user matches given query even though the user exists

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.

Django - Checking if user has logged in before

when a user signs up, they get redirected to the homepage. If it is their first time logging in, I want to display a div, however if it is not their first time, I do not want this div to appear. Currently, I am relying on date_joined andlast_login to do this check.
However this only checks dates and not the time. Therefore the div will still appear until the date has changed. I only want the div displayed once. This is when the user first logs in.
Here is my code so far:
views.py:
def home(request):
context={}
user = request.user
if user.last_login.date() == user.date_joined.date():
context['isFirstTime'] = 'isFirstTime'
else:
pass
return render(request, 'home.html', context)
template:
{% if isFirstTime %}
<div style="width: 300px; height: 300px; background-color: green;">
</div>
{% endif %}
Does anybody know how I can alter this so it works with the current time and not the current date. This way the div is only displayed when a user first logs in. Anybody know a solution? Thank you. Also date_joined and last_login are datetime objects stored in the database.
Instead of using last_login and date_joined, consider creating a UserProfile model with an attribute is_first_login, you can read more about UserProfile here .
In your models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
is_first_login = models.BooleanField(default=True)
In your view:
def home(request):
context={}
user = request.user
if user.profile.is_first_login:
context['isFirstTime'] = 'isFirstTime'
user.profile.is_first_login = False
user.profile.is_first_login.save()
else:
pass
return render(request, 'home.html', context)
if (user.last_login - user.date_joined).total_seconds() < 5:
... # do your code
user.last_login = datetime.datetime.now()
user.save()
Both last_login and date_joined are DateTime instances so you need to compare them directly. But I recommend you to add some delta, like 5 seconds in example before to be sure

Django created_at__gt=self.request.user.last_login workinly only on users already signed in.

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)

django-permission AuthorPermissionLogic not working in function based view

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

django adding fields to model form

Following is the model which I have
class OrgStaff(BaseModel):
user = models.OneToOneField(User)
member_type = models.BooleanField(help_text="1. Read/Write 0. Read Only")
task = models.ForeignKey(ToDos, null=True, blank=True)
org = models.ForeignKey(Org)
# TODO Add possible activities
def __unicode__(self):
return self.user.username
Following is the forms file
class AddStaffForm(ModelForm):
class Meta:
model = OrgStaff
exclude = (
'task',
'org'
)
and this is how I process the view
if request.POST and form.is_valid():
form.save()
ret_url = reverse("some_view", kwargs={
'var':var,})
return redirect(ret_url)
return render(request, "form.html", {"form":form})
This form would render a dropdown, which will show all the users in the database, and a radio box.
But actually, I want to create the form, so that I can add a new user(first name, last name, username, email and password) and then the rest of the fields from the abvoe AddStaffForm.
So question basically boils down to adding fields of userform to the addstaffform.
And then handling them into the views.
Is it doable, or will I have to do it manually?
Can the above model form be extended so that I can first fill in a user detail, and then assign a type to him??
Please let me know, thanks.
Use two separate forms - UserForm, created out of models.User & AddStaffForm but exclude the 'user' field in the AddStaffForm. Use only ONE submit button for both.
So your template will look like:
<form method="post" action="/path/to/wherever">
{{ user_form }}
{{ add_staff_form }}
<button>Submit</button>
</form>
Then, when the user submits the form, process each form independently in the following order:
Process the user form first and save the user instance created by the form to the db. if user_form.is_valid() is True, you can do this by simply doing user = user_form.save()
Next, process the AddStaffForm but pass commit=False (i.e. staff = add_staff_form.save(commit=False) since it does not contain the value for the user field just yet. Provide the user values using staff.user = user and then staff.save()
Provided all other fields in the staff form are provided for (i.e. add_staff_form.is_valid() is otherwise True, this should result in the creation of a new staff instance written to db.
Hope this helps. :)