Djangoesque approach to determining submitter of form - django

In Django, you can define form classes and have templates render them according to the class fields. However, how do you securely pass in information about the user who submitted the form into the view?
For instance, you can manually hard-code the user into a form field in a template with
{{ request.user.username }}
However, couldn't anyone just submit their own post request to the form url with any user they wanted in this case?
Is there a "Django approach" to prevent the scenario above from happening and to have the view retrieve information about the user who submitted the form?

To securely track the submitter of the form, you can require that a user is logged in in order to submit the form.
As kathikr mentioned, you can use #login_required decorator to ensure that a user is logged in to submit the form. Then you can rely on request.user to determine the logged in user.

The contrib apps “django.contrib.auth” and “django.contrib.sessions” are the standard Django way of doing this.
Have a read through the documentation on the “auth” app in particular:
https://docs.djangoproject.com/en/dev/topics/auth/default/

Related

How to add permissions to edit own in django admin based on value in model?

I have a question about Django-Admin. Every item in my Django admin has got the "EngagementManager" field, which contains the name of the person. When you log in Django, your username is the same as the field, which I mentioned previously.
I need to implement the feature that login user could change/edit only items when the field "EngagementManager" will match the login user. Can someone please provide me the piece of code and some little guide where to put it, please?
Django Admin is not meant for that purpose
Django Admin should only be used when the user has complete access to everything in the database. What they can edit can be restricted, but generally what they can see should not be.
It’s not intended to allow much in terms of only allowing access to certain bits of data. It is recommended that you build a custom frontend for this purpose, where it is easy to do such restrictions.
Such restriction is easy in views and templates. Use request.user.
I am on my phone right now, but if you want, I can post some sample code which does this. Just comment below.
These are samples from an updateprofile method which I have.
The core concept here is that the only data being sent to the form is that of the user of the account which is currently logged in. You would want to implement such functionality.
views.py checking for the correct user
#login_required(login_url='/login')
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
if user_form.is_valid():
user_form.save()
return redirect('/accounts/{}'.format(request.user.username), request.user.username)
else:
print("Something broke")
else:
user_form = UserForm(instance=request.user) #grabbing the data from that specific user, making sure that is all that is passed.
return render(request, 'profile_update.html', {
'user_form': user_form,
})
In the template, the if statement checks to see whether the user of the page is the owner of the logged in account (and checks that they are looed into their account) and if so, shows them the information.
template code for checking for the correct user
{% if page_username == user.username and user.is_authenticated %}
<p>Whatever content you wanted to show to the user who owned the page and was logged in.</p>
{% else %}
<p>Whatever you want to say to users who are not authorized to view the data on the page, if anything.</p>
{% endif %}
The Django Admin is only meant for trusted administrators and content editors, not regular users.
If you need to restrict users to only be able to view their own content, then you should build that yourself.
You can achieve this by implementing custom logic in get_queryset
See this reference:
allow users view own objects in django admin

Django all-auth app, how to modify /account/profile page to display custom view

After setting up the All-Auth app, when user logs in, he gets redirected to: accounts/profile/ page which tells us that the view doesn't exist.
I'm trying to figure out what kind of thing to include there, and decided to allow the user to change their basic information.
I have a users app with a Teacher model, which is set up as follows:
class Teacher(models.Model):
user = models.ForeignKey(User, unique=True)
rate = models.CharField(max_length=200)
availability = models.BooleanField(default=False)
verified = models.BooleanField(default=False)
I want the accounts/profile/ page to show a form, containing this information. The user can edit these fields and also edit their Firstname, Lastname and Email, which belong to a different Model - User.
I can't seem to get started on this. When I created a detailed view for the profile page, I get an error saying:
No PK or SLUG provided
I want Django to change the current users info, not based on the primary key in the URL. Do I need a custom view? I've looked at [other solutions1
but they seem to be utilising the private key parameter.
What I need is a working view function, something similar to (not working):
def get_teacher_info(request):
current_user = request.user
teacher = get_object_or_404(Teacher, username=current_user.username)
return render(request, 'account/profile.html', {
'user':current_user,
'teacher': teacher,
'error_message': "The field is blank",
})
and in the accounts/urls.py I've added:
url(r"^profile/$", views.get_teacher_info, name="account_profile"),
but when I make calls like {% teacher.rate %} in the html template, I get:
Invalid block tag on line 5: 'teacher.rate'. Did you forget to register or load this tag?
The def get_teacher_info(request) function and the urls.py entry are working. Looks like the issue may be in the template. Use {{ instead of {% tags. So use {{ teacher.rate }} not {% teacher.rate %} in the template.
Redirecting to /accounts/profile/ After the login is the default behavior of Django. django-allauth is using the same default behavior from Django settings.
Now if you want to modify this default redirect url then change it to
LOGIN_REDIRECT_URL = '/custom/redirect-url'
Important Note: Without a starting / you're redirecting to a path that is appended to the CURRENT URL. You need to use a leading slash to redirect to a path that is appended to the domain root. further details on it. In short, without a starting / you will end up with
../login/callback/custom.redirect-url (appended to current url)

Restricting Access to Django's success_url()

New Django user here.
I am trying to restrict access to Django's success_url() upon GET requests. I realize I am not the first to ask this question, however, I am specifically trying to achieve this in conjunction with Django's generic class-based views. Generic views seem cleaner, faster, and more idiomatic. I want to use them as much as possible unless they are absolutely unfit for the job.
Basically, I am creating a simple contact form for non-users who only want to send me a message without creating an account. I've created a contact app to handle these types of contacts. I've created a ModelForm, which I am rendering with a contact.html with Django's FormView. After a person submits the form, they will receive a cool looking thank you message, rendered with a thanks.html, which has its own url.
But I only want them to see the thank you message if they POST the contact form. Currently, you can go on my site and type '/contact/thanks/', and my thanks.html will be rendered whether you've submitted a form or not. Django's success_url apparently defaults to a GET request.
Here's my view:
class MyContact(FormView):
template_name = 'contact.html'
form_class = ContactForm
success_url = 'thanks'
Here's my form:
ContactForm(forms.ModelForm):
class Meta:
model = Contact
fields = ['email_address', 'message_body']
Here's the html form in contact.html:
<form action="" method="POST">{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="send btn btn-secondary">Send Message</button>
</form>
My first thought was to decorate my contact app url with a require_POST() decorator like this:
urlpatterns = [
url(r'^thanks/$', require_POST(views.ThanksPageView.as_view()), name='thanks'),
]
This doesn't work because Django's success_url() defaults to a GET request.
What is the most idiomatic way to approach this? Since Django is a 'batteries included' framework, I do not want to use 'duck-tape', meaning I do not want implement any ad-hoc logic in my views or urls.
Sorry if I've missed anything in the docs or questions archive.
Since you're asking for a idiomatic approach, I would consider the messages framework and specifically the SuccessMessageMixin. This would allow you to add a success message to e.g. the contact form itself and the url-pattern for the /thanks/ page would not be necessary.

Two separate template profile pages for user his/herself and other users?

Since profile pages should be editable for the user himself/herself,
should I determine the profile owner in view and have a different template just for other users viewing the same page
or
should I use template tags determining whether the current user is the profile owner?
I'm a newb to web app development and Django. Sorry if the question is too broad. Thanks.
You can use a single template and check if user is authenticated in your template and display the necessary code for logged-in users.
To check if user is authenticated in a template, use user.is_authenticated. But, remember that the auth context processor has to be enabled in the settings for the current user to be present in the template context.
You can pass a user_id kwarg in the url to access that user's profile page. You can define your urls something like:
url(r'^user/profile/(?P<user_id>\w+)/$', ProfilePage.as_view(), name='profile_page'),
Then in your views, you can pass the requested_profile_id in the context.
Class ProfilePage(..):
def get_context_data(self):
context = super(ProfilePage, self).get_context_data()
# pass user id for which profile page was requested
context['requested_profile_id'] = self.kwargs.get('user_id')
return context
Then in your templates, check if current user's id is same as the requested_profile_id. If it is same, then you can display the portion to edit profile. You can do something like:
my_template.html
...
<!-- your normal code here -->
..
{% if user.is_authenticated and user.id==requested_profile_id %}
...
<!-- your code for current user profile edit page here -->
..
{% endif %}
You show the himself/herself users profile data based on their userid,so single profile template is enough.
Example:
def profile(request,userid)
.......
return render_to_response('profile.html',{..context..})

providing current user to all templates in django project

I wonder if there is a way to provide all templates in django application with the current user variable so that I manage login and logout functionality in all pages.
It is a very common practice so there must be an obvious solution I have missed.
I know about the RequestContext Class but that means I have to add it to every view in the application which seems impractical.
May be a custom template tag?
There's a template context processor for this: django.core.context_processors.auth. Check out the documentation, you can access the current user and the permissions in the templates.
You can indeed use a custom tag to achieve that.
Note that by default the custom tag has no access to the request object and therefore to the user data. In order to gain access to the request you can define the custom tag with the takes_context attribute and grab the request from the context:
Something like:
#register.inclusion_tag('userdata.html', takes_context = True)
def userdata(context):
request = context['request']
user = request.user
return {'user':user}
You can just use {{ request.user }} to display the name of the current user, or maybe for example {{ request.user.is_authenticated }} to find out if it's an AnonymousUser or not.