Django: forbid to add the same data for user - python

I have app where user can add url to favorite in database with ManyToMany relationship. So every user can have a lot of urls and every url can have a lot of users. I have problem with creating restriction to avoid adding the same url for user. I mean I want to create the mechanism where url can be added a lot of time for users, but only one per user.
In my models I have:
from django.db import models
from django.contrib.auth.models import User
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
repositorys = models.ManyToManyField(User,related_name='user')
And in my views:
def favorites(request):
url = request.session.get('url')
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
repos = user.users.all()
return render(request, 'favorites.html',{'url':url,'repos':repos})
Favorites function is called on clicked button in my template. When I click the above function is executed and redirect to /favorites. The problem is, when I click again on button and Im logged as the same user, this url is added again to database. Same problem with refreshing favorites.html. Is there any logic way to solve this problem?
EDIT:
view favorites:
def favorites(request):
url = request.session.get('url')
try:
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
except IntegrityError as e:
return render_to_response('base.html')
repos = user.users.all()
return render(request, 'favorites.html', {'url': url, 'repos': repos})
My idea here was to return all urls for user then move adding to try block. When there is a IntegrityError then move to base.html and later will display some messages

Use unique_together in your model to manage uniqueness at the database level
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
class Meta:
unique_together = [['url', 'user']]
then when you want to add a URL for a specific user, handle unique error to prevent a user to add a specific URL twice or more.
try:
# add a Repository
except IntegrityError:
# raise an error

Related

How can I do the Unit testing of these custom permissions in my Django/Django REST project?

everyone. I hope you're doing well. I'm a Django newbie, trying to learn the basics of RESTful development. I only know Python, so this is my best fit for the moment.
Right now I'm trying to implement Unit tests for my API. It's a simple model to implement CRUD on the names and heights of NBA players. In my models I added a class to describe this data and translated it to a view with ModelViewSets. I wanted to make this data editable only for a specific type of user (a read-write user), only readable for another (read-only user) as well as unaccesible to non-authenticated users. To do so, I created a custom User Model and translated it to my views with a custom permission. Now I want to write a few Unit tests to check that:
r/w user can create a new player
r/w user can get a list of players
r/o user cannot create a new player
r/o user can get a list of players
unauthenticated user cannot create a new player
unauthenticated user cannot get a list of players
Here is my models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
ROLES = [('read-only', 'read-only'), ('read-write', 'read-write'),]
role = models.CharField(max_length=32, choices=ROLES, default='read-only')
# Create your models here.
class NBAplayers(models.Model):
first_name = models.CharField(max_length=100)
h_in = models.DecimalField(max_digits=5, decimal_places=2)
h_meters = models.DecimalField(max_digits=5, decimal_places=2)
last_name = models.CharField(max_length=120)
def __str__(self):
return self.first_name
And my views.py:
from .models import NBAplayers, User
from .serializers import NBAplayersSerializer
from rest_framework.response import Response
from rest_framework import status, viewsets, permissions
class ReadOnlyPermission(permissions.BasePermission):
def has_permission(self, request, view):
requests = ('POST', 'PUT', 'DELETE', 'PATCH')
user = request.user
role = User.role
if user.is_anonymous: # Not Authenticated
return request.method == 'GET'
else:
if role == 'read-write':
return request.method in requests + ('GET',)
else: # Read Only User
return request.method == 'GET'
class NBAPlayersViewSet(viewsets.ModelViewSet):
serializer_class = NBAplayersSerializer
queryset = NBAplayers.objects.all()
permission_classes = [ReadOnlyPermission]
And Finally, my urls.py:
from django.contrib import admin
from django.urls import path, include
from .views import NBAPlayersViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('players', NBAPlayersViewSet, basename = 'Players list')
urlpatterns = [
#djoser basic authentication
#Routers URLs
path('', include(router.urls)),
path('players/<int:pk>/', include(router.urls)),
path('', include('djoser.urls')),
path('', include('djoser.urls.authtoken')),
]
All the code above required to add the custom user to the global settings with AUTH_USER_MODEL = 'api_basic.User'. So, I've read the documentation and watched a few videos trying to understand how to write the proper tests, but the examples are not that close to this problem. I wanted to ask for a few pointers in the right direction, so that I can use it to build the rest of the tests. This is my first time writing unit tests.
Thank you beforehand for any help or input you can give. Cheers!
There's nothing tricky about this. For example, let's say I have a service that is only available to Admin/Staff users. It means you need to be both logged in and be an Admin.
In my test suite, I'll simply create a test_permissions method where I'll do something like:
Be logged out
Try the service, assert failure
Create normal user and log him in
Try the service, assert failure
Create admin user and log him in
Try the service, assert success
To give you a more concrete example, here's a snippet example with DRF. Do note that I'm using custom functions and 3rd party libraries to do some stuff, but the logic remains the same:
class TestCaseSuite(Base):
def test_permissions(self):
user = UserFactory()
admin = AdminFactory()
# 401 Not authenticated
self.api_client.logout()
response = self.api_client.post(url, data=data)
assert response.status_code == 401
# 403 Not admin
self.api_client.force_authenticate(user)
response = self.api_client.post(url, data=data)
assert response.status_code == 403
# 201 Admin
self.api_client.logout()
self.api_client.force_authenticate(admin)
response = self.api_client.post(url, data=data)
assert response.status_code == self.success_code
# ...
# other tests for that service like:
# testing custom fields
# testing a successful call and the response output
# testing side-effects, like when sending an email or whatnot
# etc
A few things to note:
In THIS test, I'm ONLY testing permissions. I'll use other tests for testing field validation, response output/format, etc.
If you find yourself needing a specific user for several tests, you could create it in the setUp method (which is triggered before EVERY test)
So, if you want to test specific permissions, you simply have to create the necessary objects/users and assert your expectations:
user with THIS data should fail
user with THAT data should succeed
etc.

Returning a filter result in a different page - Django

I've searched up but nothing seams to do the trick or be on point.
Let's say i have two websites on page A.html I have a filter (djnago_filter) and B.html is empty. If the user submits the search form he is redirected from page A.html to page B.html where the results are displayed with for loop.
How do I to that?
I was thinking about passing the data from query set to another view but there must be better solution to this.
There are several ways to store and display information on the different views(pages):
You could use the Sessions:
#views.py
#set the session key in the view A:
request.session['info'] = {'key', 'value'}
#get the session key in the view B:
info = request.session['info']
del request.session['info']
You could use Models with JSONField:
#app/models.py
#model to store information from page A
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User,
on_delete=models.DELETE,
related_name="info")
info = models.JSONField()
#views.py
#store data in the view A:
from app.models import MyModel
MyModel.objects.create(user=request.user, info={'key', 'value'})
#retrieve data in the view B:
info = MyModel.objects.get(user=request.user)
But actually, all depends on the logic that you intent to achieve. There are actually other methods like using async Ajax, React, WebSockets...
you can pass it as an HTML request parameter inside the url, for example, in your pageA template will be
to page B
and inside pageb view, you can take the filter from the request object
def page_b_view(request):
some_filter = request.GET.get('some_filter')

How to block user from opening pages if user is not logged in

I have login and work page.
I want user to login first and then redirect to work page. This scenario is working fine for me.
However when I try hitting the work page it is giving me error, I am checking it through session variable which I know I am doing it wrong as I am not sure which user is trying to access the page if user tries to hit the work page directly as there will be many users in database. How can I restrict user to hit the work page directly and if user does it should redirect to login page
views.py file method is as follows:-
def chatbox_index(request):
context={}
template_name = "bot/chatbot.html"
**<I have to check here if user session is active or not?>**
return render(request, template_name,context=context)
else:
return render(request,'' ,context=context)
after user login I am creating session and storing in below 2 variables:-
request.session['employee_name'] = employee.usrFirstName
request.session['employee_id'] = employee.id
Django provides login_required decorator for this:
from django.contrib.auth.decorators import login_required
#login_required
def chatbox_index(request):
context={}
template_name = "bot/chatbot.html"
return render(request, template_name,context=context)

Django 1.10 query a table using username from logged in user

I'm new to Django, creating a site where I want logged in users to see there own data provided in a table. This table has a field username.
I want the users to see there own data in a listview. I can't figure out how I can query, using the username from User. To give you an idea of what I am doing, this is what I have as code: (I tried multiple other ways, but I can't get a string with the User login Name.
from django.contrib.auth.models import User
from django.views.generic import ListView
username = User.username
class RoosterListView(LoginRequiredMixin, ListView):
queryset = Roosters.objects.filter(startvc__range=(DatumStart, DatumEind),username=CurrentUser).order_by("startvc")[:35]
Thanks so much in advance.
Remove the username = User.username line - User is the model class, not the current user instance.
You can access the current user if you set queryset, as this is loaded when the module is imported, not when the request is made. If you override the get_queryset method, you can access the user with self.request.user.
class RoosterListView(LoginRequiredMixin, ListView):
def get_queryset(self):
return Roosters.objects.filter(startvc__range=(DatumStart, DatumEind), username=self.request.user.username).order_by("startvc")[:35]
you can get the username of your logged in user by
username = request.user
you can simply pass your request parameter around to get all the information of the current session and do whatever query you wanna do.

Reuse change password from auth User

I have the following subclass:
class UserProfile(User):
user = models.OneToOneField(User, parent_link=True)
And an UserProfileAdmin:
class UserProfileAdmin(admin.ModelAdmin):
# stuff
admin.site.register(UserProfile, UserProfileAdmin)
This admin displays a change password link /admin/customer/userprofile/1548/password/ under the password field. But i get the following error:
invalid literal for int() with base 10: '1548/password'
I want to use the same change password form as in auth.User and after change is made i want to be redirected to UserProfileAdmin. How can i do that?
It is the expected behavior because:
/admin/customer/userprofile/1548/password/
Wants to display the change form for userprofile with id '1548/password'.
Extending the User class is not the way to store extra data per user. Read the documentation on Storing additional information about users for instruction on how to do it the right way.
That said, you can if you want this url to open the admin change password page, you can do a redirect as such:
# put **before** include(admin.site.urls)
url(r'/admin/customer/userprofile/(?P<id>\d+)/password/$', 'views.redirect_to_password'),
And in views.py:
from django import shortcuts
def redirect_to_password(request, id):
return shortcuts.redirect('/admin/auth/user/%s/password/' % id)
If you also want to redirect the /admin/auth/user/1234 to /admin/customer/userprofile/1234, then you could add this:
url(r'/admin/auth/user/(?P<id>\d+)/$', 'views.redirect_to_customer_changeform'),
Would work with a similar view:
from django import shortcuts
def redirect_to_customer_changeform(request, id):
return shortcuts.redirect('/admin/customer/userprofile/%s/' % id)

Categories