I've created a simple 'favourites' system in my django app. I have events which can be faved by any logged in user. What i'm trying to do is somehow check if current logged-in user has faved currently looped event.
It would be quite easy if i could make it in my views.py file but i need to check it directly in template or in a model (in which i can't get currently logged-in user data).
My Event model looks like that:
...
class Event(models.Model):
...
users_faved = models.ManyToManyField(User, related_name='users_faved', blank=True)
If i would be able to access currently logged in user data from Model, i would create a new object called, for example, is_faved() which would be called from a template that way:
{% for event in evemts_list %}
{{ event.is_faved }}
{% endfor %}
But as i've mentioned before, i'm not able to access current logged in user data. Any help would be appreciated! Thanks
Try writing a custom template filter, that takes an event and the user as an argument.
Related
I am just starting to work with Django and I have some problems with forms and dropping lists.
I have a model with two attributes, and I want to display one of the attributes in a dropping down list (this one will be unchangeable) and another one in a text field (this one will be changeable). Also, I have a submit button, so I want to change a second attribute in a text field and by pressing on the button. How can I do this? What would some examples be?
As you are starting to work with Django, you might or might not know about how Django handle forms.
In Django, forms can be handled in two ways:
User-created and managed forms (without any form class)
Class-managed forms (connected to Django models)
Documentation form Django Forms
Now let’s talk about the first type of forms (where you create your HTML form and manage the request sent to server):
These forms are simple to make and when there are only a few and are only suggested when you have a very small amount of inputs (I would say four or fewer inputs).
Here is a simple example of subscription of a newsletter with an email example.
<form id='sub-form' method="POST">
{% csrf_token %}
<div>
<input type="email" name="sub_email">
</div>
<input class="button" value="Subscribe" type="submit" id="subbutton">
</form>
So a very important thing to look at here is {% csrf_token %}, about which you can read more about here and about how it works and prevents cross-site request forgery. This token will be required when you make a request to Django server with any post request and data.
In this subscription form you see one <input> with name="sub_email". Take note of this as we will use this to get this value on the server as this is the key to its value, and then a simple Submit Button.
When you press Submit on a page let’s say url = "http://BASE_URL/home" you will receive a POST request on the view that handles that URL.
So now coming to the view.py, let’s say you only allow registered users to subscribe then the view will go something like this (assuming you are not expecting any other request from the home URL).
def home(request):
user=request.user
if request.method == "POST":
if user.is_authenticated:
email = request.POST['sub_email'] #Using name of input
#Logic to save this email
return HttpResponse("You are Subscribed",status=200)
else:
return HttpReposnse("You are not Authenticated",status=401)
else:
return render(request,"home.html")
Now as you are the expert of simple forms, let’s work with Django class-based forms.
These views are a little work when you have very few inputs, but they are a great help in manageability and when you have to work with large number of inputs.
You will request these Class Based Forms as in your question you are trying to send an instance of a model from your Models.py to a form to user.
I have a model of Posts that can be used for this example:
class Post(models.Model):
postTitle = models.CharField(max_length = 90,null=True)
subTitle = models.CharField(max_length = 160,null=True)
location = models.CharField(max_length = 3,default = 'IN',null=True)
Now according to your question, you are trying to let the user change one attribute, let’s say postTitle and for location you are not letting the user select one of the countries which is preselected and for your post.
Now we have to create a form for this. Forms in class based are created in Forms.py. If you don't have forms.py then you can create one right along models.py and views.py.
Now for the form, I would like to edit some existing data as you are saying one of the attributes (Fields) is fixed and another editable, but you get the value from the model.
class PostEditForm(ModelForm):
location = forms.CharField(label='Country ',widget=forms.Select(attrs={'class': 'Classes_HERE','placeholder':' Select a Country','disabled':'disabled'} ,choices=country_list),required=True)
class Meta:
model = Post
fields= ['postTitle','subTitle','location']
labels = {
'postTitle':'Title',
'subTitle':'Sub-Title',
}
widgets = {
'postTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Title'}),
'subTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Sub-Title'})
}
Attributes can be mentioned in forms fields the way I have mentioned them in the above example. I used disabled="disabled" to disable (not editable) location field and used forms.Select to make it drop down.
You might also see that I gave the location field a list to choose from. This is how you can create a list of your items. It's been quite some time when I wrote this, so there might be errors or it may not work for you, so just make sure you are referring to the current documentation and searching Stack Overflow for answers.
country_list = [
('', 'Select a Country'),
("AF", "Afghanistan"),
("AX", "Aland Islands"),
("AL", "Albania"),
("DZ", "Algeria"),
("AS", "American Samoa"),
("AD", "Andorra"),
("AO", "Angola"),
("AI", "Anguilla"),
("AQ", "Antarctica"),
("AG", "Antigua And Barbuda"),
("AR", "Argentina"),
("AM", "Armenia"),
("AW", "Aruba"),
.
.
.
Now this form can be passed as context in a view to an HTML page.
def editPost(request,post_id):
user=request.user
post = get_object_or_404(Post,id=post_id) #Getting the instance of Post
if user.is_authenticated:
formPost = PostEditForm(request.POST or None,instance=post)
if request.method=='POST':
if formPost.is_valid():
savedPost=formPost.save()
else:
return render(request,'postEdit.html',{'formPost':formPost})
else:
return HttpResponse("Not Authorized",status:401)
Now your HTML file postEdit.html should look something like this:
<form id="post-form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{formPost}}
</div>
</form>
That is it and adding a submit button in the same form, you can now edit your instance of post that you passed along with this {{formPost}}. Combine your logic wherever you think needs a change to fit in what you want to do.
By no means I am saying all this code is in working condition, but it is shown only to illustrate the flow and working.
I am using the Django inbuilt user authentication system, from from django.contrib.auth.models import User. And I have realised that in the admin page it always displays the username of the user. Is it possible to change the def __str__ (self): method of that function to display a customized one, something like this.
def __str__ (self):
return f"{self.first_name}"
For any such changes, you should refer to the template that's rendering it. Here, it is the Django Admin's base.html template.
As you can see in this line in the file, it searches for both short_name and user_name in that order and displays the first one available.
{% block welcome-msg %}
{% translate 'Welcome,' %}
<strong>
{% firstof user.get_short_name user.get_username %}
</strong>.
{% endblock %}
And get_short_name returns the first name of the user. So, your user does not have their first name defined and hence it's showing up their username.
NOTE : Please check your Django version's documentation since this has been implemented after version 1.5 and above, and is valid only for greater versions.
I highly recommend to extend the auth user model and thus you can do a lot of customizations sooner or later.
You can use lambda function to alter the __str__() method of auth user as
from django.contrib.auth.models import User
User.__str__ = lambda user_instance: user_instance.first_name
Note: This snippet should get executed on the Django server initialization
If you are trying to change the username which is shown at the top-right-corner (as mentioned by #amit sigh ), set the lambda function for get_short_name as,
from django.contrib.auth.models import User
User.get_short_name = lambda user_instance: f"Prefix : {user_instance.first_name} : Suffix"
I have a web app where a user signs in and begins entering items for a ToDoList. The base.html is wrapped in an is_authenticated check so users cannot see anything in the app until they have logged in. I was doing some testing with:
Logging in as User2
Adding a new ToDoListItem
Redirecting to the DetailView
In this case, the URL = http://localhost:8000/to_do_list/to_do_item/72
At this point I realized that the DetailView would allow User2 to see the details for any ToDoListItem for User1 just by entering in an existing pk into: http://localhost:8000/to_do_list/to_do_item/<int:pk>.
urls.py includes
path('to_do_item/<int:pk>', views.ToDoListItemDetail.as_view(), name='todo-item-detail'),
views.py
class ToDoListItemDetail(DetailView):
model = ToDoListItem
todolistitem_detail.html
{% extends 'base.html' %}
{% block content %}
Home
<h1>DetailView for 'ToDoListItem' model</h1>
<p>TaskTitle: '{{ object.title }}'</p>
<p>Complete: '{{ object.is_complete }}'</p>
<p>User: '{{ object.user}}'</p>
{% endblock %}
What is the recommended way to prevent this from happening? I am considering the following:
I could completely remove the DetailView and direct to a different URL that only returns the user's data (using something like ToDoListItem.objects.filter(user=request.user))
I could check that the name of the user logged in matches the name of the user that owns the ToDoListItem.
I could override get_context_data() for the DetailView and check user ownership there (similar to no. 1, but within the DetailView)
??? (Something else better than the above I do not know about yet)
Is there a way to limit a user to only see their own data throughout an application without implementing this logic every time it is needed?
You can filter in the DetailView as well, by overriding the get_queryset method [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class ToDoListItemDetail(LoginRequiredMixin, DetailView):
model = ToDoListItem
def get_queryset(self, *args, **kwargs):
return super(ToDoListItemDetail, self).get_queryset(
*args, **kwargs
).filter(user=self.request.user)
Django will, behind the curtains, always call get_queryset(..). By default this function returns the queryset of the model you specified with all objects. But you thus can filter it further down.
Django's get_object method [Django-doc] will then further filter it down with the id and/or slug, but if you already filter out the elements that do not belong to the self.request.user, then this can thus only result in an query returning no results.
It also makes sense to here add the LoginRequiredMixin [Django-doc] to your class, since in case the user did not log in, you probably want to redirect hem/her to the login screen.
There is a permission named: ¨can read¨ that allow you to handle the access. For example:
class FruitEdit(PermissionRequiredMixin,DetailView):
permission_required = 'app.read_fruit'
...
I hope you can solve it
I was wondering how might I show a new user a message such as 'First time here?' in a Django template using conditionals?
e.g.
{% if this_website_has_not_been_accessed_by_user %}
<h3>First time here?</h3>
<h5>Follow these steps...</h5>
{% endif %}
What might be the best way to do this? Thanks
Assuming you only want to show that banner at the top of the page for logged in users (those that have a User instance in the current request), you could create a Model (or, if you have your own user profile model, add a field to it) to save that.
class Access(models.Model):
user = models.ForeignKey(User)
has_been_here_before = models.BooleanField(default=False)
In your view you would have to check that Model to see if the user is registered there and has the flag set.
See this post for detecting first login.
See also this similar post for detecting a visit to each individual page or view.
EDIT: according to your comments you want to show the welcome banner to anonymous users (not logged in) if they visit for the first time.
You could achieve that using djangos session framework. You can use request.session just like a simple dict.
In your home page view (or through a custom middleware so it affects all pages not just the home page) you could check if a user has already dismissed the welcome banner or not.
def my_home_view(request):
...
# as default 'show_welcome_banner' will be True
context['show_welcome_banner'] = request.session.get('show_welcome_banner', True)
...
Then, in your template use:
{% if show_welcome_banner %}
<h3>First time here?</h3>
<h5>Follow these steps...</h5>
{% endif %}
Your banner could have a 'dismiss' button that posts a hidden to a different url (maybe even through ajax). This url would alter the users session info:
def dismiss_welcome_banner(request):
...
if request.method == 'POST':
request.session['show_welcome_banner'] = False
...
Notes:
if a user access the site through incognito mode (the session cockie gets deleted when he closes the browser) he will need to click the dismiss button every time he opens the incognito browser.
You could additionally check that loggen in users do not get to see that banner, depending of how you prefer it.
I need a certain context variable in my base.html. This is to contain a set of usernames, e.g. [name1, name2, name3, name4,]. If a logged in user's username is part of this list, I give said user certain preferential treatment and show something in the navbar.
To achieve this, I wrote a template tag:
from django import template
from django.db import models
from django.contrib.auth.models import User
register = template.Library()
VIPS = [name1, name2, name3, name4,]
#register.simple_tag
def verified(user):
return VIPS
register.simple_tag(verified)
And then in base.html, I added {% load verified %} at the top, and then:
{% if user.username in verified %}
<!-- do something -->
{% endif %}
This isn't working. What am I doing wrong? I suspect I've written my template tag incorrectly, but I've tried several, more complex approaches (in vain), at least this simpler one made logical sense to me.
My project's a legacy Django 1.5 project with Python 2.7.
You don't need the register.simple_tag(verified) line, as the #register decorator is already doing that.
However, you might consider a different approach to avoid additional processing in the template, assuming your user is coming from request.user...
#regsiter.assignment_tag(takes_context=True)
def check_user_is_verified(context):
user = context['request'].user
return user and user in vips
Then in your template:
{% check_user_is_verified as is_verified %}
{% if is_verified %}
{# whatever #}
{% endif %}
By leveraging an assignment tag, you can check if the user is verified once, and leverage the context variable you assign instead of having to perform the same list processing each time.
Another alternative is to use a cached property on a custom User object, or a "Profile" model that is linked to your User model via a OneToOneField.
from django.utils.functional import cached_property
class Profile(models.Model):
user = models.OneToOneField(User)
#cached_property
def is_verified(self):
# get the list of vips here
return self.user in vips
If your list of vips changes, just clear the cache key, which you could do via a signal or a Celery task, etc:
del profile_instance.is_verified
Now you have a very efficient property you can check anywhere in your code. My preference tends to be fat models, skinny views and dumb templates.