drf-yasg Customize SwaggerUIRenderer - django

I'd like to customize the style (font, colors, logo, etc) of drf_yasg generated docs.
I see that I can extend drf_yasg/swagger-ui.html with the blocks extra_head, extra_styles, extra_body, extra_scripts, and can even overwrite the other blocks if I need to.
What I am not clear on is how I point to my template that extends swagger-ui.html.
I started with
class MyCustomSwaggerUIRenderer(SwaggerUIRenderer):
template = 'api/custom-swagger-ui.html'
I want to replace SwaggerUIRenderer with MyCustomSwaggerUIRenderer in get_schema_view but do not understand how/where to do it without explicitly trying to enumerate all the other Renderers required too in some subclass of rest_framework.views.APIView and that seems convoluted.
Pointers to docs or examples are appreciated. I've already read https://drf-yasg.readthedocs.io/ without success.

You don't have to create a custom class for this. You just need to create a directory with the name drf-yasg under new or existing app and then place a file with the name swagger-ui.html underneath it with your custom template. For example, if you already have an app with the name api, you can just put it under api/templates/drf-yasg/swagger-ui.html. Make sure the app api is specified before drf-yasg in INSTALLED_APPS.
Reference: https://github.com/axnsan12/drf-yasg/issues/294#issuecomment-464461773

Related

Specify typing for Django field in model (for Pylint)

I have created custom Django model-field subclasses based on CharField but which use to_python() to ensure that the model objects returned have more complex objects (some are lists, some are dicts with a specific format, etc.) -- I'm using MySQL so some of the PostGreSql field types are not available.
All is working great, but Pylint believes that all values in these fields will be strings and thus I get a lot of "unsupported-membership-test" and "unsubscriptable-object" warnings on code that uses these models. I can disable these individually, but I would prefer to let Pylint know that these models return certain object types. Type hints are not helping, e.g.:
class MealPrefs(models.Model):
user = ...foreign key...
prefs: dict[str, list[str]] = \
custom_fields.DictOfListsExtendsCharField(
default={'breakfast': ['cereal', 'toast'],
'lunch': ['sandwich']},
)
I know that certain built-in Django fields return correct types for Pylint (CharField, IntegerField) and certain other extensions have figured out ways of specifying their type so Pylint is happy (MultiSelectField) but digging into their code, I can't figure out where the "magic" specifying the type returned would be.
(note: this question is not related to the INPUT:type of Django form fields)
Thanks!
I had a look at this out of curiosity, and I think most of the "magic" actually comes for pytest-django.
In the Django source code, e.g. for CharField, there is nothing that could really give a type hinter the notion that this is a string. And since the class inherits only from Field, which is also the parent of other non-string fields, the knowledge needs to be encoded elsewhere.
On the other hand, digging through the source code for pylint-django, though, I found where this most likely happens:
in pylint_django.transforms.fields, several fields are hardcoded in a similar fashion:
_STR_FIELDS = ('CharField', 'SlugField', 'URLField', 'TextField', 'EmailField',
'CommaSeparatedIntegerField', 'FilePathField', 'GenericIPAddressField',
'IPAddressField', 'RegexField', 'SlugField')
Further below, a suspiciously named function apply_type_shim, adds information to the class based on the type of field it is (either 'str', 'int', 'dict', 'list', etc.)
This additional information is passed to inference_tip, which according to the astroid docs, is used to add inference info (emphasis mine):
astroid can be used as more than an AST library, it also offers some
basic support of inference, it can infer what names might mean in a
given context, it can be used to solve attributes in a highly complex
class hierarchy, etc. We call this mechanism generally inference
throughout the project.
astroid is the underlying library used by Pylint to represent Python code, so I'm pretty sure that's how the information gets passed to Pylint. If you follow what happens when you import the plugin, you'll find this interesting bit in pylint_django/.plugin, where it actually imports the transforms, effectively adding the inference tip to the AST node.
I think if you want to achieve the same with your own classes, you could either:
Directly derive from another Django model class that already has the associated type you're looking for.
Create, and register an equivalent pylint plugin, that would also use Astroid to add information to the class so that Pylint know what to do with it.
I thought initially that you use a plugin pylint-django, but maybe you explicitly use prospector that automatically installs pylint-django if it finds Django.
The checker pylint neither its plugin doesn't check the code by use information from Python type annotations (PEP 484). It can parse a code with annotations without understanding them and e.g. not to warn about "unused-import" if a name is used in annotations only. The message unsupported-membership-test is reported in a line with expression something in object_A simply if the class A() doesn't have a method __contains__. Similarly the message unsubscriptable-object is related to method __getitem__.
You can patch pylint-django for your custom fields this way:
Add a function:
def my_apply_type_shim(cls, _context=None): # noqa
if cls.name == 'MyListField':
base_nodes = scoped_nodes.builtin_lookup('list')
elif cls.name == 'MyDictField':
base_nodes = scoped_nodes.builtin_lookup('dict')
else:
return apply_type_shim(cls, _context)
base_nodes = [n for n in base_nodes[1] if not isinstance(n, nodes.ImportFrom)]
return iter([cls] + base_nodes)
into pylint_django/transforms/fields.py
and also replace apply_type_shim by my_apply_type_shim in the same file at this line:
def add_transforms(manager):
manager.register_transform(nodes.ClassDef, inference_tip(my_apply_type_shim), is_model_or_form_field)
This adds base classes list or dict respectively, with their magic methods explained above, to your custom field classes if they are used in a Model or FormView.
Notes:
I thought also about a plugin stub solution that does the same, but the alternative with "prospector" seems so complicated for SO that I prefer to simply patch the source after installation.
Classes Model or FormView are the only classes created by metaclasses, used in Django. It is a great idea to emulate a metaclass by a plugin code and to control the analysis simple attributes. If I remember, MyPy, referenced in some comment here, has also a plugin mypy-django for Django, but only for FormView, because writing annotations for django.db is more complicated than to work with attributes. - I was trying to work on it for one week.

generating thumbnails in Alfresco 4.0.d

I am using the Java web services in Alfresco Community 4.0.d and currently looking to add thumbnail functionality to my site. I noticed the thumbnails are not available immediately after posting a new image; I was wondering if someone can recommend a good approach to have the generation triggered manually?
Answered your question in the forums as well. Using the JavaScript API you can ask a document to generate its thumbnail, like this:
document.createThumbnail("doclib");
In this case, "doclib" is the name of the thumbnail configuration for the document library thumbnails in Share, but this could be any thumbnail definition you've created.
Docs live at http://docs.alfresco.com/4.0/topic/com.alfresco.enterprise.doc/references/API-JS-Thumbnail-createThumbnail.html
For Java, look at the org.alfresco.repo.thumbnail.CreateThumbnailActionExecuter class source. In your own class you could do something similar. Or, better yet, use the actionService to invoke the create-thumbnail action.
To use the Action Service, all you need is the name of the action and the parameters it expects. For example, here's what it looks like when you use the mail action:
ActionService actionService = getServiceRegistry().getActionService();
Action mailAction = actionService.createAction(MailActionExecuter.NAME);
mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, ExternalReviewNotification.SUBJECT);
mailAction.setParameterValue(MailActionExecuter.PARAM_TO, recipient);
mailAction.setParameterValue(MailActionExecuter.PARAM_FROM, ExternalReviewNotification.FROM_ADDRESS);
mailAction.setParameterValue(MailActionExecuter.PARAM_TEXT, sb.toString());
actionService.executeAction(mailAction, null);
To do this for thumbnails, you'll use "create-thumbnail" for the name (or CreateThumbnailActionExecuter.NAME). Looking at the source for that class I see that it takes two parameters, PARAM_CONTENT_PROPERTY, which would be "cm:content", and PARAM_THUMBNAIL_NAME which would be "doclib" for the normal document library thumbnail or your thumbnail name if you've defined your own.
One thing to note, in the executeAction call, that second argument is the "actioned upon noderef". In your case, you'll want that to be the node you are generating the thumbnail for.

Django: Model-dependent Apps to Choose From Based on User

I have a Django app where multiple teams upload content that will be parsed. The app keeps track of certain common information in the parsed content. The issue here is that each team has content that needs to be parsed by a different parser because the content is in a different format (e.g. some teams have XML content, some have text, some have JSON, etc...). Each of the teams has provided a parser (a python module) that grabs the necessary info that is placed into corresponding Django models after parsing.
My question is: what is the best way to architect this in Django where each team can have their own parser setup correctly? It can be purely backend, no need for a user form or anything like that. My first thought was that I would create a Parser model with ForeignKey to each team like so:
class Parser(models.Model):
team = models.ForeignKey('Team')
module_path = models.CharField(max_length=..., blank=False)
and that module_path would be something like "parsers.teamA.XMLparser" which would reside in my app code in that path like so:
parsers/
teamA/
__init__.py
XMLparser.py
teamB/
Then when my app is coming to parse the uploaded content, I would have this:
team = Team.objects.get(id=team_id)
parser = Parser.objects.get(team=team)
theParser = __import__(parser.module_path)
theParser.parse(theStuffToBeParsed)
What problems does anyone see with this? The only other option that I can think of is to create separate Django apps for each parser, but how would I reference which team uses which app in the database (same as done here?)?
The approach you're taking seems valid to me. Keep in mind that you are effectively running arbitrary python code so I would never be using something like this on a public facing site and ensure you trust the teams writing their parsers.
You might make this a bit nicer by writing a custom Field to represent the module path that will remove the need to handle the import everytime and will instead handle the import for you and return the parse method (or maybe even better a Parser object where you could tell the teams to implement an interface)
The best example might be to look at the source for django's ImageField or even CharField. Instead of having having your model have a CharField, you'd have a "ModuleField": parser = ModuleField(). The database stored value would indeed be the path to the module (so simply make it a subclass of CharField), but override the to_python method. In your new to_python method handle importing the module and return a python object.
That python object could be anything you want it to be, from your example you could return theParser.parse. This would result in if you have a Parser instance foo you could the_parser_method = foo.parser

Django-tastypie creating a URL hierarchy

I'd like to create a URL hierarchy using Tastypie but am running into some errors. Here's how I'd like the hierarchy to work:
/recipe
/recipe/ID
/recipe/ID/spice
/recipe/ID/spice/ID
I can't find out how to do this. When I set this up following the Tastypi instructions my URLs would be like this:
/recipe
/recipe/ID
/spice
/spice/ID
If I change the resource_name for spice to "/recipe/spice" then I get a "NotFound: Invalid resource lookup data provided (mismatched type)" error.
Any suggestions about what I could do?
Tastypie is meant to help implement a REST API, and thus by default only supports URLs that conform to REST practices. Namely, each URL should contain a resource name ('recipe' or 'spice') and optionally an identifier for that resource ('ID'). Anything outside of this breaks from REST practices and if you're not implementing a REST API you may want to re-consider whether or not you should be using Tastypie.
That being said, Tastypie does provide a ton of hooks for customizing things. For custom URLs, you'll want to define the method override_urls to map certain URLs to custom views and do some pre-processing before sending it to the regular dispatchers.
If possible, I'd recommend just using standard REST practices and break things up as separate 'recipe' and 'spice' resources. If you need to filter on recipes based on spices that are in them, 'spices' should be passed in as a GET parameter rather than part of the base URL. Hope that helps.

django pdf export

I want to generate a PDF which will show the output of my queryset in table format, for example:
query = ModelA.objects.filter(p_id=100)
class ModelA(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
p_id = models.IntegerField()
description = models.TextField()
I need to show the values for name, description and pid in the generated PDF.
As mentioned by other people the best way to do this is to generate a template then convert the result, using one of the many libraries around, into a PDF. This method provides you with the usual amount of control over your templates, for example using tags.
I've used the previously mentioned ReportLab/Pisa setup but found it to be quite limiting, most layouts have to be built using tables and many parts of the CSS2 spec aren't implemented yet.
An easier to use library is wkhtmltopdf which is a headless distribution of WebKit. This has the benefit of rendering your templates like any webkit browser would and thus letting you use webkit specific extras, such as parts of the CSS3 spec that exist in WebKit.
Using the wrapper library django-wkhtmltopdf you can render_to_pdf in your view instead of the usual Django render_to_response.
Disclaimer: I am a contributor to this library.
Update
This library has been converted to CBVs and most of the information below (which I'll leave to help add some context) is now implemented in the library itself for convenience.
See the quickstart docs for an example of how to implement the below code block. If you need to use more advanced usage you can subclass PDFTemplateView and add various options like filename and the margins.
An example view:
from django.shortcuts import render_to_response
from wkhtmltopdf import render_to_pdf
def pdf(request):
context.update({'objects': ModelA.objects.filter(p_id=100)})
kwargs = {}
if request.GET and request.GET.get('as', '') == 'html':
render_to = render_to_response
else:
render_to = render_to_pdf
kwargs.update(dict(
filename='model-a.pdf',
margin_top=0,
margin_right=0,
margin_bottom=0,
margin_left=0))
return render_to('pdf.html', context, **kwargs)
The conditional statement here lets you pass ?as=html to the view so you can develop in the browser. It's a bit of an ugly way to do it currently but there are plans to fix this in a release soon.
Using this view you could loop the contents of objects in your view as you would normally and even extend your base template. I've normally used a different stylesheet specifically for the PDFs for maintainability and readability of the styles as you need to do a few things differently for PDFs, such as setting a min-height if you want to keep your footer block in the same place.
On this note, you can create header and footer templates that will be used on each page of your PDF by passing them into render_to_pdf as part of kwargs.
Just generate the HTML the way you would want it and ask xhtml2pdf to convert it into pdf.
Like one of the comment mentions, there is ReportLab, but that would need you to give specification in PDF's required format. It provides you more control, but if it is easier for you to generate HTML, the standard way which you need to generate anyway, you can use pisa to make it pdf.
Try wkhtmltopdf with python-pdfkit
This seems to be the best solution.Supports javascript and css