How to make the output "pretty" using Django Rest Framework? - django

Is there a way to format output that comes out from Django Rest Framework? What I'm looking for is a Django/DRF equivalent to PHP JSON_PRETTY_PRINT
Currently, the output looks like this:
{"id":1,"username":"bartalamej","city":"Ostrava","photo":"uploads/avatars/a84232eff3aa407db95ff792aec77414.jpg"}
But I'd like it to look like this:
{
"id":1,
"username":"bartalamej",
"city":"Ostrava",
"photo":"uploads/avatars/a84232eff3aa407db95ff792aec77414.jpg"
}
Does anybody know how to achieve this?

You should override the view's get_renderer_context and set an indent:
def get_renderer_context(self):
context = super().get_renderer_context()
context['indent'] = 4
return context
This will add an indent to the json.dumps call.
Alternatively, you can also leave that up to your client and add the indent within the Accept header as explained in the documentation

The BrowseableAPIRenderer formats JSON nicely for browsing. More information can be found here.

Related

How to recognize urls with get parameters un django?

I am learning Django, usually I use urls like www.mysite.com/post/1, and I have understood them because they are well described in the documentation.
Now I need to create a url like: www.mysite.com/calendar/?day=21&?month=12&year=2020, I don't understand what should I write in my ulrpatterns list.
I have tried something like this:
url(r'^search/(?P<parameter>\w+)', views.calendar, name='calendar')
but it doesn't work.
What should I write as regex expression?
Thanks
These parameters are not part of the path, you can't capture them using the url patterns.
You can access them directly inside your view by using request.GET, see the docs.
A common pattern is the following:
def calendar(request):
day = request.GET.get("day") # Will be None if "day" isn't in the query
month = request.GET.get("month")
year = request.GET.get("year")
[...]

Flask Swagger documentation query parameter GET required

I'm using Swagger documentation with my flask project to document the endpoints and parameters.
To define the query parameters for an endpoint I'm doing:
#api.doc(params={
'name_query_parameter': 'Description'})
I wanted to know if it's possible for that parameter to show in the docs as "required", like it does for when the parameter is part of the path (home/name_query_parameter/something/something).
Looking into the documentation I only found the following:
#api.expect()
#api.doc(body=the_defined_payload)
But this implies for the information to be on the body, I can't have that with a GET request. Plus, I want it as a query parameter, not as part of the payload.
Is this possible at all?
Thanks.
The final solution to this is as follows, thanks to Mikhail for commenting about the parser. I have to admit though, documentation is not the best for flask-restplus.
I used the params part to make sure the fields appear in the docs along with a description and the parser for custom validation and to make the field appear as required even though it is located in the URL as params.
parser = reqparse.RequestParser()
parser.add_argument('superimportant',
type=inputs.boolean, location='args', required=True)
parser.add_argument('something', type=custom_validation_parser, location='args')
class MySuperClassResource(Resource):
#api.doc(parser=categories_by_retailer_parser,
params={"superimportant": "Description of this important field",
"something": "bla bla"
})
def get(self, blable):
parser.parse_args()
pass
The custom_validation_parser is just a method that allows custom validation, like for empty values. The format of that method is as follows. (It must return the value you want to access, and if there's . problem, raise a ValueError).
def custom_validation_parser(value):
if not value:
raise ValueError("Must not be empty.")
return value

Generate url for Django Simple History historical object

Given an model called Stuff, I want the url to a HistoricalStuff object.
In other words, how does one implement get_historical_url in the below code snippet?
stuff = Stuff.objects.first()
stuff.pk
-> 100
historical_stuff = stuff.history.first() # we want to get url for this
historical_stuff.pk
-> 1
get_historical_url(historical_stuff)
-> /path/to/admin/stuff/100/history/1
Obviously the dumb solution would be to use a format string but I'd rather use urlresolvers
After much digging around, I found in the simple history source code that the url name is similar to the admin change names, namely admin_%s_%s_simple_history.
With this knowledge, get_historical_url looks like
def get_simplehistory_url(history_obj):
parent_obj = history_obj.history_object
return urlresolvers.reverse('admin:{}_{}_simple_history'.format(
parent_obj._meta.app_label, parent_obj._meta.model_name), args=(parent_obj.id, history_obj.pk))

Django syndication framework: prevent appending SITE_ID to the links

According to the documentation here: https://djangobook.com/syndication-feed-framework/
If link doesn’t return the domain, the syndication framework will
insert the domain of the current site, according to your SITE_ID
setting
However, I'm trying to generate a feed of magnet: links. The framework doesn't recognize this and attempts to append the SITE_ID, such that the links end up like this (on localhost):
<link>http://localhost:8000magnet:?xt=...</link>
Is there a way to bypass this?
Here's a way to do it with monkey patching, much cleaner.
I like to create a separate folder "django_patches" for these kinds of things:
myproject/django_patches/__init__.py
from django.contrib.syndication import views
from django.contrib.syndication.views import add_domain
def add_domain_if_we_should(domain, url, secure=False):
if url.startswith('magnet:'):
return url
else:
return add_domain(domain, url, secure=False)
views.add_domain = add_domain_if_we_should
Next, add it to your INSTALLED_APPS so that you can patch the function.
settings.py
INSTALLED_APPS = [
'django_overrides',
...
]
This is a bit gnarly, but here's a potential solution if you don't want to give up on the Django framework:
The problem is that the method add_domain is buried deep in a huge method within syndication framework, and I don't see a clean way to override it. Since this method is used for both the feed URL and the feed items, a monkey patch of add_domain would need to consider this.
Django source:
https://github.com/django/django/blob/master/django/contrib/syndication/views.py#L178
Steps:
1: Subclass the Feed class you're using and do a copy-paste override of the huge method get_feed
2: Modify the line:
link = add_domain(
current_site.domain,
self._get_dynamic_attr('item_link', item),
request.is_secure(),
)
To something like:
link = self._get_dynamic_attr('item_link', item)
I did end up digging through the syndication source code and finding no easy way to override it and did some hacky monkey patching. (Unfortunately I did it before I saw the answers posted here, all of which I assume will work about as well as this one)
Here's how I did it:
def item_link(self, item):
# adding http:// means the internal get_feed won't modify it
return "http://"+item.magnet_link
def get_feed(self, obj, request):
# hacky way to bypass the domain handling
feed = super().get_feed(obj, request)
for item in feed.items:
# strip that http:// we added above
item['link'] = item['link'][7:]
return feed
For future readers, this was as of Django 2.0.1. Hopefully in a future patch they allow support for protocols like magnet.

Ignore params in urls

I need to bolt a quick city-specific thing onto a site I am currently building. I am going to do it something like this - http://example.com/XX/normal-slug. What I have set up in my urls.py is this:
url(r'^(?P<city>[a-zA-Z]{2})/', include('homepage.urls', namespace='homepage')),
url(r'^(?P<city>[a-zA-Z]{2})/section/', include('section.urls', namespace='section')),
# etc
The problem I am encountering now is that all of a sudden my methods all are now expecting a "city=XX" param. I plan to process the actual city business logic in a middleware. My question is... is there anyway have django "ignore" the named param? I don't want to modify all my views now to take either **kwards or 'city' param. If I hard code the city code, it does what I expect:
url(r'^XX/section/', include('section.urls', namespace='section')),
So can I replicate that behaviour, but dynamically?
(Also, I plan on something more robust further down the line, probably Django Sites)
You can use a non-capturing regex to accept the parameter but not pass it to the views.
r'^[a-zA-Z]{2}/section'
Set the param as optional in the regexp with ?:
url(r'^((?P<city>[a-zA-Z]{2})/)?section/', include('section.urls', namespace='section')),
If city is not sent in the URL, your view will receive city=None