How to base queryset off of current user django rest serializer - python-3.x

I'm trying to create a serializer with DRF that is able to validate if a user has access to a primarykeyrelatedfield entry.
I have a separate function which returns a queryset of the files the user can access. All it needs as a parameter is the request object. I'd like to use this function as the queryset kwarg for the primarykeyrelatedfield.
However, I can't find a way to access "self" in this location, so there doesn't seem to be a way to define a Queryset which is dependent upon the current user for a serializer.
This is my current attempt, which fails since when calling _request(self) I cannot access self.
class MySerializer(serializers.Serializer):
def _request(self):
request = getattr(self.context, 'request', None)
if request:
return request
files = serializers.PrimaryKeyRelatedField(many=True, required=True, queryset=get_user_files(_request(self)))
I want to validate that the user has access to the file(s) they are referencing in the request. How would I do this?

I ended up settling on a slightly less clean answer than I'd have liked:
class MySerializer(serializers.Serializer):
files = serializers.PrimaryKeyRelatedField(many=True, required=True, queryset=ScanFile.objects.all())
def validate_files(self, value):
request = self.context.get('request')
queryset = get_user_files(request)
for file in value:
if not queryset.filter(pk=file.id).exists():
raise ValidationError({'Invalid file': file})
return value
This seems to be a bit inefficient, as it ends up querying for each file twice, but it achieves the affect of users can only access files they specifically have request to.

Related

How to use PermissionRequiredMixin in FBV?

I am thinking about how I can use PermissionRequiredMixin in FBV.(I have used the same in CBV and it is working as expected).
Please find the FBV(here need to implement permission). I don't want to use #login_required()
#login_required()
This will check only if the user is authenticated or not.
def delete_viewgen(request,PermissionRequiredMixin,id):
oneuser=ShiftChange.objects.get(id=id)
permission_required = ('abc.add_shiftchange',)
oneuser.delete()# delete
return redirect('/gensall')
I am getting ERROR : missing 1 required positional argument: 'PermissionRequiredMixin'
CBV Case where it is working fine.
class ShiftChangeUpdateView(PermissionRequiredMixin,UpdateView):
permission_required = ('abc.change_shiftchange',)
login_url = '/login/'
model=ShiftChange
fields='__all__'
In CBV it is working fine and if user is not having change permission it is giving 403 Forbidden how to implement same in FBV and also how to customize 403 Forbidden message.
Thanks!
On function based views you would use decorators, in your particular case
permission_required decorator
#permission_required('abc.change_shiftchange', raise_exception=True)
delete_viewgen(request,id)

Django Rest Framework DjangoModelPermissions not working properly

Why the DRF permission class DjangoModelPermissions allow every users to perform all requests like
POST, PUT, DELETE even those user i didn't manually assign them the add, change, delete permissions from my django admin. they are allowed to be only view the objects but why the are getting all unsafe request POST, DELETE... ?
Views.py
class HotelViewSet(viewsets.ModelViewSet):
queryset = Hotel.objects.all()
serializer_class = HotelSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [DjangoModelPermissions]
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissions',
]
}
The viewsets.ModelViewSet does not implement delete(), post(), etc. methods itself. If you want to use it, you have to implement them yourself.
Instead, you should use Django REST generics. Please refer to Django REST framework docs:
https://www.django-rest-framework.org/api-guide/generic-views/
I would suggest using DRF generic views where you can use specific views like ListAPIView/ RetrieveAPIView for GET requests, CreateAPIView for POST, UpdateAPIView for PUT, etc.

How can I create an upload_to folder that is named a field belonging to the model that is simultaneously being created?

Context: I'm building a car dealership app using Django 3.1.4, and trying to implement a feature where by every time a new ad is created, so is a new folder (via the upload_to method) within /media/ that will only contain that newly created ad's photos.
I've gotten this working somewhat using date formatting (see below) but I need this new folder to be named one of the fields belonging to the model(see the second code snippet).
photo_4 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
For example, every newly created ad would use the title field..
class Vehicles(models.Model):
CONDITION_CHOICES = [("Brand New","Brand New"),("Used", "Used")]
FUEL_CHOICES = [("Petrol","Petrol"),("Diesel", "Diesel"),("Electric", "Electric"),("Hybrid", "Hybrid")]
TRANSMISSION_CHOICES = [("Automatic","Automatic"),("Manual", "Manual")]
title = models.CharField(max_length=200, default = "Ad Title & Folder Name")
def folder_name(self, title):
return self.title
make = models.CharField(max_length=100)
Ideally then, the media folder would look something like
/media/
Opel Astra VXR/
Mercedes E200/
I understand that many others have done something similar but all the examples I found use the 'user' and instead I need to use a model field (belonging to the model that is being created) as the folder name.
Closing notes:
The function in the second code snippet was just something I was trialing.
Also, this is still in development, so in prod I fully intent to direct all media to an S3 bucket down the line.
As documented
upload_to may also be a callable, such as a function. This will be
called to obtain the upload path, including the filename. This
callable must accept two arguments and return a Unix-style path (with
forward slashes) to be passed along to the storage system. The two
arguments are:
instance ( An instance of the model where the FileField is defined. )
filename ( The filename that was originally given to the file )
For example:
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
Found the soloution and wanted to post it for the next person who might need it. It turned out to be a syntax issue on my formatting . This did the trick and now opens a new folder for each ad and their photos. It also updates the original folder with any new photos added to the preexisting ad.
Title and function below:
title = models.CharField(max_length=200, default = "Ad Title & Folder Name")
def upload_photo_to(self, filename):
return f'{self.title}/{filename}'
Photo field and how it was applied:
photo_1 = models.ImageField(upload_to=upload_photo_to, blank=True)

Securely using a variable in URL in a Flask webapp

I am using Flask to make a webappthat will contain a variable within the URL (example below).
#app.route('/landingpage/<id>') # /landingpage/A
def landing_page(id):
# Storage of a hashed <id>...
My question relates to the variable within the URL which could contain confidential information, which should not be accessible to anyone other than the person entering the URL.
Would it be sufficient for the connection to be made over HTTPS to prevent anyone else accessing the variable prior to it being stored in a hashed format?
You can use UUID in url, flask have support for this type of url, and i recommend to use this lib flask-uuid to gerate the uuid to your client access
from flask_uuid import FlaskUUID
flask_uuid = FlaskUUID()
flask_uuid.init_app(app)
#app.route('/personalID')
def gerate_uuid():
return make_response({'uuid':uuid.uuid4()})
#app.route('/landingpage/<uuid:id>') # /landingpage/A
def landing_page(id):
return id # 'id' is a uuid.UUID instance

Get username from local session Telethon

I'm using telethon library to crawl some telegram channels. While crawling, i need to resolve many join links, usernames and channel ids. To resolve these items, i used method client.get_entity() but after a while telegram servers banned my crawler for resolving too many usernames. I searched around and found from this issue, i should use get_input_entity() instead of get_entity(). Actually telethon saves entities inside a local SQLite file and whenever a call to get_input_entity() is made, it first searches the local SQLite database, if no match found it then sends request to telegram servers. So far so good but i have two problems with this approach:
get_input_entity() just returns two attributes: ID and hash but there are other columns like username, phone and name in the SQLite database. I need a method to not just return ID and hash, but to return other columns too.
I need to control the number of resolve requests sent to telegram server but get_input_entity() sends request to telegram servers whenever founds no match in the local database. The problem is that i can't control this method when to request telegram servers. Actually i need a boolean argument for this method indicating whether or not the method should send a request to telegram servers when no match in the local database is found.
I read some of the telethon source codes, mainly get_input_entity() and wrote my own version of get_input_entity():
def my_own_get_input_entity(self, target, with_info: bool = False):
if self._client:
if target in ('me', 'self'):
return types.InputPeerSelf()
def get_info():
nonlocal self, result
res_id = 0
if isinstance(result, InputPeerChannel):
res_id = result.channel_id
elif isinstance(result, InputPeerChat):
res_id = result.chat_id
elif isinstance(result, InputPeerUser):
res_id = result.user_id
return self._sqlite_session._execute(
'select username, name from entities where id = ?', res_id
)
try:
result = self._client.session.get_input_entity(target)
info = get_info() if with_info else None
return result, info
except ValueError:
record_current_time()
try:
# when we are here, we are actually going to
# send request to telegram servers
if not check_if_appropriate_time_elapsed_from_last_telegram_request():
return None
result = self._client.get_input_entity(target)
info = get_info() if with_info else None
return result, info
except ChannelPrivateError:
pass
except ValueError:
pass
except Exception:
pass
But my code is somehow performance problematic because it makes redundant queries to SQLite database. For example, if the target is actually an entity inside the local database and with_info is True, it first queries the local database in line self._client.session.get_input_entity(target) and checks if with_info is True, then queries the database again to get username and name columns. In another situation, if target is not found inside the local database, calling self._client.get_input_entity(target) makes a redundant call to local database.
Knowing these performance issues, i delved deeper in telethon source codes but as i don't know much about asyncio, i couldn't write any better code than above.
Any ideas how to solve the problems?
client.session.get_input_entity will make no API call (it can't), and fails if there is no match in the local database, which is probably the behaviour you want.
You can, for now, access the client.session._conn private attribute. It's a sqlite3.Connection object so you can use that to make all the queries you want. Note that this is prone to breaking since you're accessing a private member although no changes are expected soon. Ideally, you should subclass the session file to suit your needs. See Session Files in the documentation.

Resources