Django: How to edit model without PK in URL? - django

I'm writing a multiuser app, the problem is if I Edit a Product I have the URL:
re_path(r'backend/produkter/edit/(?P<artikel_id>[0-9]+)/$', backend_views.edit_artikel_view),
Which results:
http://127.0.0.1:8000/backend/produkter/edit/36/
Now another User can use this URL because he is authenticated, and it looks not clean to me. Is it possible to go from view / url:
http://127.0.0.1:8000/backend/produkter/
to
http://127.0.0.1:8000/backend/produkter/edit/
and give the ID on another way to the editView?

I think one way to do in your requested format is to use Django Session.
Flow will be like this:
When you click Edit Product, make a post request to the same view with parameter as 'edit_product_id' and value as selected product's id
Set session variable for edit_product_id in the same view function like this:
edit_product_id = request.POST.get('edit_product_id')
request.session['edit_product_id'] = edit_product_id
after this redirect to the editView
On product editView, retrieve the edit_product_id and use it to update the databse.
edit_product_id = request.session.get('edit_product_id')

If this product can not be modified by another user, then for sure this will be related with the logged-in User somehow.
1.) In that case, you don't need pk. Instead you can get the user info in the View itself from request.user, and now you can fetch the product you want to edit for this user. In this case your url will be
http://127.0.0.1:8000/backend/produkter/edit
2.) Second way can be to check the user logged in and compare it with the product user, this can be done with request.user. Something like this :
if request.user.id == product.user.id:
considering you have the this relationship between user and product.

Related

What is the best practice to let users set a variable which is then be available for all views?

The app I'm working on is a kind of an internal portal for the big Holding (Group of Companies), where various financial and non-financial data stored for all the companies of the Holding. Users are logging into the portal and on the home page they see the list of all Companies that they are authorized to see.
Then user should be able to enter into one particular Company from the list and afterwards all subsequent views should show data filtered only for that selected Company until user does not exit it and enter into another Company from home page.
I'm trying to achieve this functionality by following but I'm not sure is it proper django way to do it:
In views.py I have Mylogin view which is setting global variable Selected_Company='None' when User logs in.
Then when User selects any particular Company to work with that Selected_Company variable gets that company's name as a value.
All other views which show company related data have a django filter showing model objects matching with the value of Selected_Company.
If you know any other proper way to do it, I'd appreciate your advice.
Thx
Option 1:
One way is to have the URLs structure in a way that the company PK is present in the URL. For example:
/accounts/login/ # login; no company selected
/company/ # list of companies; no company selected
/company/14/ # company 14 was selected
/company/14/invoices/ # list of invoices of company 14
/company/14/invoices/create/ # add an invoice for company 14
/company/14/purchases/
/company/14/sales/
The urlconf would look like this:
...
url(
r'^company/',
include([
url(r'^$', views.CompanyListView.as_view()),
url(
r'^(?P<company_id>[0-9]+)/',
include([
url(r'^$', views.CompanyReadView.as_view()),
url(r'^invoices/$', views.InvoiceListView.as_view()),
url(r'^invoices/create/$', views.InvoiceCreateView.as_view()),
...
I hope that explains the point I am trying to make. The filtering in the views would be done using the company PK that is obtained from the URL. In the view code, that could look like this:
class InvoiceListView(ListView):
model = Invoice
def get_queryset(self):
return super().get_queryset().filter(
company_id=self.request.kwargs['company_id'])
Option 2:
Another way I could think of is setting the selected company in the session and using the session info to filter in the views. Maybe like this:
class InvoiceListView(ListView):
model = Invoice
def get_queryset(self):
return super().get_queryset().filter(
company_id=self.request.session['company_id'])

Using unique user identifier in Django logs

In my website I'd like to add logging of user actions (eg. user added/modified/deleted something, user visited some page, user logged-in/out). Just to be clear I'd need this identifier be unique but allow me to follow user and not change on every site (everything happens within my website).
I have already logger in place, but I'm not sure how I can get unique user identifier. Currently it looks like this:
logger.info('User %s entered website X', self.request.user)
I know I could use request.user for logged-in users, but what with the rest? Right now everyone are AnonymousUser, but instead I'd like to have some unique identifier.
I assume Django is already providing that, but question is - how I could access it.
For anonymous users I'd set my own session ID
When SessionMiddleware is activated, each HttpRequest object – the first argument to any Django view function – will have a session attribute, which is a dictionary-like object.
ref: https://docs.djangoproject.com/en/3.0/topics/http/sessions/#using-sessions-in-views
import uuid
request.session['anonymous_id'] = uuid.uuid4()
# Then you can do
logger.info('User %s entered website X', request.session['anonymous_id'])
How you decide to set this sesssion attribute is up to you, I'd suggest a middleware approach where you check, is the users logged in. If not then check if there is already a session id. If not then set one.

Django authentication how to log in the user to their project

If a user has multiple projects under their account, how do I authenticate the user so that the content is not only specific to their account, but to their default project. Ideally, the project is right at the top portion of the page, inside the base template of the site, right beside the sign-out link. The user should be able to just change the project without signing out and signing back in. The request object should be loaded with the project, so I could query the contacts model like this:
Contact.objects.filter(project = request.project)
Currently, I have to do the same query like this:
user_project = Project.objects.get(user = request.user, project_name = 'summer')
Contact.objects.filter(project = user_project)
A little nudge in the right direction is much appreciated
As far as you'r going to filter the query by request, so I suppose it's gonna be used in a view class. I had the same situation and I fixed it using Mixin view with Class Based View (CBV) and inherit it to all other my Views Class. And inside this Mixin abstract view class, you can add whatever you need and join it to the request.session dictionary-like object

How to check if the user is visiting the page for the first time

In general how do we know if user is visiting the page for the first time? Technically, do we store the number of visits to each page in the Model(I am using django) or is there some other pattern that I could follow to ease up the operation.
I m just looking for a design for implementing this.
I think you will have to create your own mechanism for this.
I would make a model storing first visits for every url and user called e.g FirstVisit. Then if a user requests a page in a view you can search if there is an entry in FirstVisit for current user and url and find out if it's his first time or not. After that, if he hasn't visited yet, you store the entry to the FirstVisit model, because he is just going to get the content of the page.
I will try and write the code:
#models.py
class FirstVisit(models.Model):
url = models.URLField()
user = models.ForeignKey('auth.User')
#views.py
def my_view(request):
if not FisrtVisit.objects.filter(user=request.user.id, url=request.path).exists():
#he visits for the first time
#your code...
FisrtVisit(user=request.user, url=request.path).save()
You can create a decorator and put in there this functionality. Then add the decorator to any view you want to store this information and from the decorator pass a flag argument to the view determining if user is there for the first time.
Following #davekr suggestion, I would create a model like FirstVisit with some attributes like ip-address, time registered and instante it on the get method (that is tied to the root url) with some validation if the user is valid or authenticated.
PS: Check this (https://docs.djangoproject.com/en/2.0/ref/request-response/) django docs to indentify the request attributes that could be of your interest to construct the FirstVisit.

Implement auto_current_user_add when using Django's User table

I have an AppEngine app that I'm migrating to run in Django, using app-engine-patch to get all the goodness of Django - particularly the Admin interface.
One of my models looks like (partially) this:
class Request(db.Model):
requestor = db.UserProperty(auto_current_user_add=True)
When I display a form based on this model I don't display the requestor field, and so when I call the Model's put() method the entity I'm saving doesn't have the requestor property set. This triggers the auto_current_user_add magic, and the user who created the request is automatically added.
Under Django, I'm using the provided Users table. I want this to display as a list of the users of my app, so the model becomes:
from ragendja.auth.google_models import User
class Request(db.Model):
requestor = db.ReferenceProperty(User)
However, this breaks the auto_current_user_add magic in the admin interface - if the user of the admin interface doesn't enter a value for the requestor property, Django sets the property to None, when I'd really like for the Request to have their username inserted automatically.
How can I restore the magic?
My solutions relies on three things:
First: it's possible to override the model's put() method.
Second: users.get_current_user() still provides the correct user, and
Third: ragendja.auth.google_models.User.get_djangouser_for_user() takes a google.appengine.api.users.user object and returns the corresponding Django User object - creating it first if it didn't already exist.
Putting this all together, I have:
class Request(db.Model):
requestor = db.ReferenceProperty(User)
def put(self):
if not self.requestor:
self.requestor = User.get_djangouser_for_user(users.get_current_user())
super(Request, self).put()
This works nicely with the admin interface: the admin can assign any existing user (or use the supplied + sign to create a new user) - if they leave it blank, they'll be assigned as the requestor.
Later when I add a view for users to manage their own requests, this value will be on the 'excluded' list, and the same method will add in their username every time they create a new request.
I'm not sure if this is an optimal solution though; I'm new to Django, so maybe there's a better way to achieve this.