Custom path parameter parsing drf-yasg and Django - python

I'm trying to force dry-yasg to properly parse parameters from path. Let's say we have
path('users/<int:user_id>/', whatever.as_view(...))
In swagger docs it is not treated as int, but string instead
I have used
swagger_auto_schema(manual_parameters = [
openapi.Parameter(
name,
openapi.IN_PATH,
description=desc,
type=openapi.TYPE_INTEGER,
required=True
)
]
but it's pretty annoying. I could not find a function/method/class responsible for parsing that. Is there a simple method to change behaviour of this parser based on path, so that if int occurs then openapi.TYPE_INTEGER will be returned instead of string?

drf-yasg determines the parameter type automatically in some situations, and falls back on string if detection fails.
queryset = get_queryset_from_view(view_cls)
for variable in sorted(uritemplate.variables(path)):
model, model_field = get_queryset_field(queryset, variable)
attrs = get_basic_type_info(model_field) or {'type': openapi.TYPE_STRING}
As you can see, it tries to get type based on column type of view queryset. If your parameter name doesn't match anything in the queryset though, you get a string. So your first choice should be to try use a name it can autodetect.
If that doesn't work however, you will need to subclass EndpointEnumerator and override get_path_parameters(), probably easiest to call super().get_path_parameters() and go though each parameter and replace type based on variable name.
To use this class you will need your own OpenAPISchemaGenerator.
use a custom OpenAPISchemaGenerator
override its endpoint_enumerator_class with your own EndpointEnumerator

Related

Access APScheduler cron trigger field values in python

Similar to this question I want to extract the info of a cron job trigger from APScheduler.
However, I need the "day_of_week" field and not everything. Using
for job in scheduler.get_jobs():
for f in job.trigger.fields:
print(f.name + " " + str(f))
i can see all the fields, e.g. week,hour,day_of_week , but
job.trigger.day_of_week is seemingly 'not an attribute' of the "CronTrigger" object. I'm confused as to what kind of object this job.trigger is and how its fields are packed. I tried to read the code on github, but it is even more puzzling.
How do I extract only the one field day_of_week, and how is this trigger class structured?
Diving deeper I found that
apscheduler.triggers.cron.fields.DayOfWeekField
I can find by indexing the job.trigger.fields[4], which seems really bad style, since it depends on the 'position'of the field. What I get is this DayOfWeekField, from which comically I am not able to retrieve it's value either:
a.get_value
<bound method DayOfWeekField.get_value of DayOfWeekField('day_of_week', '1,2,3,4')>
The structure of the fields is coded here, but I don't know what to do with dateval, the argument of get_value().
Eventually, after hopefully understanding the concept, I want to do
if job-day_of_week contains mon
if job-day_of_week == '*'
print ( job-day_of_week )
I am grateful for any suggestions/hints!
Looking at the code, you should be able to get the day_of_week field without hardcoding the index by using the CronTrigger class's FIELD_NAMES property, e.g.
dow_index = CronTrigger.FIELD_NAMES.index('day_of_week')
dow = job.trigger.fields[dow_index]
Getting the value of the field is a bit more complicated, but it appears that BaseField implements the str function that should give you the value of the expression that created the field as a string that you could parse to find what you want:
dow_value_as_string = str(dow)
if 'mon' in dow_value_as_string:
# do something
if dow_value_as_string = "*":
# do something else

Is it a good practise to use underscore to ignore a value that is being passed to a function, which we don't need?

I am working on Django rest framework which specifies a set format for function prototype for detail_route in ModelViewSet. Some background: -
The function takes in request object and lookup_field which can be the primary key for a particular table.
#detail_route(methods=["get"], url_path="get-some-data")
def get_some_data(self, request, id=None):
return Response(get_some_data(id))
Now as you can see, I do not need request object here, So should I keep it like this? or change it to
#detail_route(methods=["get"], url_path="get-some-data")
def get_some_data(self, _, id=None):
return Response(get_some_data(id))
Here I changed request to _ to indicate that I do not need this value.
which approach should be followed? Should I let it remain as a request, or change it to an underscore?
For the method arguments I would always use the proper variable name so that in future whether I work on it or my peers if I provide this code to someone else they don't have to struggle to understand what it is.
For now you might think to ignore it but since it is a method argument it would be better to have a name it stands for.
Or, let's say you are adding a docstring where you are including and defining which parameter is what. You would yourself appreciate it if some one had:
#param request: HTTP request object
instead of:
#param _: HTTP request object
If you leave the parameter exist, then give it a meaningful name always do good, even you do not use it.
In addition, _ has special use in python, check it in the following url.
What is the purpose of the single underscore "_" variable in Python?
I'd leave it with a descriptive name. Changing it to underscore or any other non-descriptive name is not beneficial.

Django reverse routes - two optional parameters

I have a question about Django and it's routing system. I believe that it can be powerfull, but right now I am struggling with one issue I don't experience when working in other frameworks and I can't seem to get a grip on it. Worth to mention that I don't have much experience with Django at this point.
The issue is simple - I have a view which takes two optional parameters, defined like this
def test_view(id=None, grid=None):
Both parameters are optional and frequently are not passed. Id can only be an integer and grid will never be an integer (it is a special string to control datagrid when I don't want to use sessions). I have a route defined like this:
url(a(r'^test_view (\/(?P<id>\d+))? (\/(?P<grid>[^\/]+))? \/?$'), views.test_view, name='test_view'),
This works great and I am not having trouble with using one-way routes. But when I try to use the reverse function or url template tag, following error occurs:
Reverse for 'test_view' with arguments '('20~id~desc~1',)' and keyword arguments '{}' not found.
In this example I tried to find reverse without the id, just with the grid parameter. I have tried various methods of passing parameters to the reverse function:
(grid, )
(None, grid)
('', grid)
{id=None, grid=grid}
All of them result in same error or similliar one.
Is there a way to implement this in django? Maybe just disable the cool URL for the grid parameter. That is how I do it in for example Nette framework for PHP, isntead of having an url like this: 'localhost/test_view/1/20~id~desc~1' I have url like this: 'localhost/test_view/1?grid=20~id~desc~1'. This would be completely sufficient, but I have no idea how to achive this in Django.
As you note in your question, the best way to achieve this is to use standard GET query parameters, rather than doing it in the path itself. In Django you do that exclusively in the view; the URL itself is then just
url(r'^test_view$', views.test_view, name='test_view'),
and you request it via localhost/test_view?id=1&grid=20~id~desc~1. You get the params from request.GET, which is a dictionary-like object; you can use .get so that it does not raise a KeyError when the key is not provided.
def test_view(request):
id = request.GET.get('id')
grid = request.GET.get('grid')

designing my ticket api

I have a function named getTicket which take two argument id which is a number and format (string)
def getTicket(id, format):
if format == "pdf":
getTicketPDF(id) #some specialized pdf method gets called
elif format == "json":
getTicketJSON(id) #specialized json method
Now if I have to support some new format like "html" then I can create another elif for html.
But I want to generalize this code so that if in future n new method gets added I do not have to change my code
How can I design my getTicket api?
You can create a dictionary that stores the format to function mapping , like "pdf" mapping to function getTicketPDF , etc. And then in your getTicket() function you call the dictionary's value for format and call it by passing id parameter to it. Example -
funcdict = {"pdf":getTicketPDF
"json":getTicketJSON}
def getTicket(id, format):
try:
funcdict[format](id)
except KeyError:
#Handle case where format is not found in dictionary
If later you decide to add a new function for a new format, you just need to add a new mapping to the dictionary.
Your use case calls for a Strategy Pattern Implementation(PDF/JSON/HTML ticket generation strategies) which uses a Factory Pattern to obtain the correct strategy implementation class.
Here are the high-level steps -
Separate the functionality of ticket generation into a class TicketGenerator. Let this be an interface. It will have a single abstract method generateTicket()
Use a TicketGeneratorFactory to get the correct TicketGenerator instance based on the type of ticket i.e. an instance of PDFTicketGenerator, JSONTicketGenerator, HTMLTicketGenerator and so on... Each of these implemention classes have a generateTicket() implementation as per the type i.e. PDF/JSON/HTML.
This instance should be assigned to the base TicketGenerator Type.
TicketGenerator.generateTicket() would then give you the ticket in the desired format - PDF/JSON/HTML.

Why is REPEATED incompatible with REQUIRED? [duplicate]

In old google appengine datastore API "required" and "default" could be used together for property definitions. Using ndb I get a
ValueError: repeated, required and default are mutally exclusive.
Sample code:
from google.appengine.ext import ndb
from google.appengine.ext import db
class NdbCounter(ndb.Model):
# raises ValueError
count = ndb.IntegerProperty(required=True, default=1)
class DbCounter(db.Model):
# Doesn't raise ValueError
count = db.IntegerProperty(required=True, default=1)
I want to instantiate a Counter without having to specify a value. I also want to avoid someone to override that value to None. The example above is constructed. I could probably live without a required attribute and instead add an increment() method. Still I don't see the reason why required and default are mutually exclusive.
Is it a bug or a feature?
I think you are right. Perhaps I was confused when I write that part of the code. It makes sense that "required=True" means "do not allow writing the value None" so it should be possible to combine this with a default value. Please file a feature request in the NDB tracker: http://code.google.com/p/appengine-ndb-experiment/issues/list
Note that for repeated properties things are more complicated, to repeated will probably continue to be incompatible with either required or default, even if the above feature is implemented.
Im not sure what was intended, heres is the "explanation" from appengine.ext.ndb.model.py:
The repeated, required and default options are mutually exclusive: a
repeated property cannot be required nor can it specify a default
value (the default is always an empty list and an empty list is always
an allowed value), and a required property cannot have a default.
Beware that ndb has some other really annoying behaviour ( Text>500 Bytes not possible without monkey-patching the expando-model, filtering by .IN( [] ) raises exception, ..).
So unless you need the speed-improvements by its caching you should might consider staying with ext.db atm.

Categories

Resources