django rest framework - custom renderer - python

I'm trying to tweak the existing XMLRenderer to create a custom one -
class CustomRenderer(renderers.BaseRenderer):
"""
Renderer which serializes to CustomXML.
"""
media_type = 'application/xml'
format = 'xml'
charset = 'utf-8'
def render(self, data, accepted_media_type=None, renderer_context=None):
"""
Renders *obj* into serialized XML.
"""
if data is None:
return ''
stream = StringIO()
xml = SimplerXMLGenerator(stream, self.charset)
xml.startDocument()
xml.startElement("job id='string1'", {})
self._to_xml(xml, data)
xml.endElement("job")
xml.endDocument()
return stream.getvalue()
def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
for item in data:
xml.startElement("string2", {})
self._to_xml(xml, item)
xml.endElement("string2")
elif isinstance(data, dict):
for key, value in six.iteritems(data):
xml.startElement(key, {})
self._to_xml(xml, value)
xml.endElement(key)
elif data is None:
# Don't output any value
pass
else:
xml.characters(smart_text(data))
For string1 I want it to get the value from the view that's calling it. string1 = the primary key from the GET in the API.
ie. if I'm calling http://localhost/API/2345 then string1 = 2345
For string2 I want it to return the model name similar to what they are doing in the following post -
Adding root element to json response (django-rest-framework)
Which is customizing the values returned by the Renderer so that the root value of the JSON/XML can be set as the model name.
I've tried tweaking the CustomRenderer to contain the lines but then when running my view it complains that "object() takes no parameters" on my views.py -
if request.method == 'GET':
DEV = Trgjob.objects.using('database1').filter(job_id=pk).order_by('job_order')
serializer = CustomSerializer(DEV, many=True)
return CustomRenderer(serializer.data)

It looks like you're trying to return the renderer instance from the view?
You should just be returning regular data from the view, but set the renderer classes using the renderer_classes attribute on the view class.
class MyView(APIView):
renderer_classes = [CustomRenderer]
...
def get(self, request, pk=None):
DEV = Trgjob.objects.using('database1').filter(job_id=pk).order_by('job_order')
serializer = CustomSerializer(DEV, many=True)
return Response(serializer.data)
Edit: As to getting extra context into the renderer from the view... you can do that by inspecting renderer_context['view'], so something along these lines in your render() method...
view = renderer_context['view']
job_id = view.kwargs['job_id']

Related

get parameters in a get request in django rest framework?

I want to get the parameters sent to my rest api
what I want is to obtain the parameters that to use them consume another api and return the response of the third party api
but in name and comic i get None
http://127.0.0.1:8000/searchComics/
{name:"3-D Man","comics":12}
this is my view
class MarvelApi(APIView):
def get(self, request):
private_key = "88958f2d87bd2c0c2fa07b7ea654bcdf9f0389b3"
public_key = "8d415ffcc9add56b0a47c0a7c851afc3"
ts = 1
md5_hash = "46ecbbd63108b0561b8778a57823bd34"
query_params = self.request.query_params
name = query_params.get('kword', None)
comic = query_params.get('comic', None)
end_point = f"https://gateway.marvel.com:443/v1/public/characters?ts={ts}&apikey={public_key}&hash={md5_hash}&name={name}&comic={comic}"
response = requests.get(end_point)
response_json = json.loads(response.text)
return Response(status=status.HTTP_200_OK, data=response_json)
I think the problem is these two lines
name = query_params.get('kword', None)
comic = query_params.get('comic', None)
that do not capture the values ​​correctly, do you know how to solve it?
You wanted to get them from GET method, but instead you gave a dictionary, so I guess you sent it via POST. Instead of posting dictionary you should go with url:
http://127.0.0.1:8000/searchComics/?name=3-D+Man&comic=12
And you had probably a typo. You had plural "comics" in dictionary and you seek for "comic" singular.
And if you want to have data with POST method, just change def get(...) to def post(...).

With Flask-Admin and Flask how can I submit a form\view based on ModelView from code?

With Flask-Admin and Flask how can I submit a form\view based on ModelView from code?
I'm trying to create a separate view\form that would allow user to add multiple entries with one form. Specifically allowing to upload multiple images with common prefix name and common parameters. I'd like to do it by submitting a single-image upload form from code, because it does some additional processing like resizing images and I'd like to let Flask-Admin handle connecting database entries and files.
Here's the form I'd like to submit from code:
class ImageView(ModelView):
def _list_thumbnail(view, context, model, name):
if not model.path:
return ''
return Markup('<img src="%s">' % url_for('media',
filename=form.thumbgen_filename(model.path)))
column_labels = dict(show_in_header="Show In Header?",
path="Image")
form_create_rules = ("name",
"tags",
rules.Text(
"Use this image as header. If more than one image is selected header image will be random each time the page is loaded."),
"show_in_header",
"path")
form_excluded_columns = ("timestamp")
column_formatters = {
'path': _list_thumbnail
}
thumbnail_size = config("media", "thumbnail_size")
form_extra_fields = {
'path': BroImageUploadField('Image',
base_path=IMAGES_FOLDER,
thumbnail_size=(thumbnail_size, thumbnail_size, True),
endpoint="media",
url_relative_path='media',
relative_path=None)
}
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
# redirect to login page if user doesn't have access
return redirect(url_for('login', next=request.url))
And here I'm creating a form and I'm just not sure that .process() is the function to submit it? Is there one at all?
lass MultipleImagesUploadView(BaseView):
#expose("/", methods=["GET", "POST"])
def index(self):
if request.method == "POST":
a_form = MultipleImagesForm(request.form)
base_name = a_form.base_name.data
tags = a_form.tags.data
show_in_header = a_form.show_in_header.data
print (request.files.getlist(a_form.images.name))
uploaded_files = request.files.getlist(a_form.images.name)
for i, uf in enumerate(uploaded_files):
try:
name, ext = os.path.splitext(uf.filename)
if ext not in IMAGE_EXTENSIONS:
flash("Image file {} was skipped as it's extension is not supported ({}).".format(uf.filename, ext), category="warning")
continue
image_contents = uf.stream.read()
image_form = ImageView()
image_form.name.data = "{}_{}".format(base_name, i)
image_form.tags.data = tags
image_form.show_in_header.data = show_in_header
image_form.path.data = image_contents
image_form.process()
except Exception as e:
flash ("Unhandled exception: {}".format(e), category="warning")
flash("Images were added to the gallery.", category='success')
a_form = MultipleImagesForm()
print("############", a_form)
return self.render('/admin/multiple_images_upload.html', form=a_form)
I can't figure out a way to submit a form from code, been trying to find the answer in docs and google for hours now with no luck.
Found the issue. In my case I was missing the enctype="multipart/form-data". Without that files part was sent as empty.
Also changed to using from flask_wtf import FlaskForm and enabling it as {{ form.files(class="form-control", multiple="") }} in the template.
Files can then be accessed with uploaded_files = request.files.getlist("files") on POST request, it will hold array of file-objects.
I hope this helps someone. If any additional formatting is required I will add or expand the answer.

Django syndication function error

I'm trying to create a custom Django RSS feed using django syndication (actually using django wagtail feeds). I have an error which I think I've identified as stemming from a NoneType object which is returned by the get_object() function inside syndication/views.py.
`AttributeError at /feed/basic/Chups/
'NoneType' object has no attribute 'startswith'
Exception Location: /Users/technical/.virtualenvs/wagtest4-plnzODoN/lib/python3.6/site-packages/django/contrib/syndication/views.py in add_domain, line 19`
That function is called as part of class Feed() and looks like this:
def get_object(self, request, *args, **kwargs):
return None
That function is called at line 36 but fails because get_object() returns a None object.
My customisation of django wagtail feeds extends Feed in the following way:
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import (
SyndicationFeed, rfc3339_date, Rss201rev2Feed
)
from .models import RSSFeedsSettings, RSSFeed
class BasicFeed(Feed):
# FEED TYPE
feed_type = Rss201rev2Feed
def get_object(self, request, category):
return category
try:
feed_app_settings = RSSFeedsSettings.objects.get(feed_category_name="Flex")
print(feed_app_settings)
feed_app_label = feed_app_settings.feed_app_label
feed_model_name = feed_app_settings.feed_model_name
feed_category_name = feed_app_settings.feed_category_name
use_feed_image = feed_app_settings.feed_image_in_content
except: # pragma: no cover
feed_app_settings = None
try:
feed_model = apps.get_model(app_label=feed_app_label,
model_name=feed_model_name)
except: # pragma: no cover
feed_model = None
# The RSS information that gets shown at the top of the feed.
if feed_app_settings is not None:
title = feed_app_settings.feed_title
link = feed_app_settings.feed_link
description = feed_app_settings.feed_description
author_email = feed_app_settings.feed_author_email
author_link = feed_app_settings.feed_author_link
item_description_field = feed_app_settings.feed_item_description_field
item_content_field = feed_app_settings.feed_item_content_field
def items(self, obj):
url_category = obj
categories = ContentType(app_label="blog", model="blogcategory")
category_id = categories.get_object_for_this_type(name=url_category).id
return feed_model.objects.filter(categories=category_id).order_by('-date').live()
def item_pubdate(self, item):
return datetime.combine(item.date, time())
def item_link(self, item):
return item.full_url
def item_author_name(self, item):
pass
urls.py includes this and requests seem to be reaching the function fine.
url(r'^feed/basic/(?P<category>[0-9a-zA-Z]+)/$', BasicFeed(), name='basic_feed'),
Can anyone tell me why that might be? I'm missing something about the expected functioning of this. Thanks!

how to convert httpresponse object to dictionary

first of all i'm sorry because of my duplicated question but actually the other didn't work for me at all.
my problem is that I have 2 views which the first one is returning a Httpresponse to the 2nd, and what I want is to convert this Httpresponse to dictionary in the 2nd view and have access to it's elements.
here is the code :
1st view:
def base_tag_upload(request, tag):
error = False
upload_msg = "success"
user = request.user
response_data = {"error": error, "upload_msg": upload_msg, "file_id": None, "file_version": None}
if request.method == 'POST':
form = UploadForm(request.POST or None, request.FILES or None, tag=tag, user=user)
if form.is_valid():
cd = form.cleaned_data
uploaded_file = cd['file']
collection_name = cd['new_name'] or uploaded_file.name.split('.')[0].strip()
response_data.update(
{"uf_name": uploaded_file.name, "uf_size": uploaded_file.size, "uf_colname": collection_name})
set_primary = True # first file in collection
# Finding/Creating Related FileCollection
collection = FileCollection.objects.create(name=collection_name)
is_major = cd['version_type'] == 'mj'
file_obj = collection.upload_file(uploaded_file, set_primary, Major_version=is_major)
file_obj.author = user
collection.tags.add(tag)
collection.get_tag_permissions(tag, False)
file_obj.get_collection_permissions(collection, False)
set_user_ownership(collection, tag, user)
set_user_ownership(file_obj, tag, user)
collection.save()
file_obj.collection = collection
file_obj.save()
response_data.update({'file_id':file_obj.id, 'file_version': file_obj.version})
ActionLog.log(action=Action.objects.get(name="create"), target=file_obj,
user=user, request=request, details=None, extra_details=None)
redirect_url = reverse('file_history', kwargs={'collection_id': collection.id})
response_data.update({'redirect': redirect_url})
return HttpResponse(json.dumps([response_data]))
and the 2nd one :
def tag_upload(request, tag_id):
try:
tag = Tag.objects.get(id=tag_id)
except Tag.DoesNotExist:
return HttpResponse(simplejson.dumps([{'error': 'value_error', 'upload_msg': "no such folder"}]))
k = base_tag_upload(request, tag)
k.response.decode('utf-8')
print k
return base_tag_upload(request, tag)
but I got this error when I wanted to decode the Httpresponse as shown above :
AttributeError: 'HttpResponse' object has no attribute 'response'
I have 2 views which the first one is returning a Httpresponse to the 2nd
Then you didn't structured your code properly - a view has no business calling another view not messing with the response.
If you need to share some common code between two views, then extract this code in a distinct function (that would in this case return a plain dict) and call this function from both your views.
It's k.content.decode('utf-8').

Why doesn't my tastypie cache get called?

I'm looking at the tastypie caching docs and trying to set up my own simple caching thing, but the cache doesn't seem to get called. When I visit http://localhost:8000/api/poll/?format=json, I get my tastypie generated json, but I don't get the output from the cache class.
from tastypie.resources import ModelResource
from tastypie.cache import NoCache
from .models import Poll
class JSONCache(NoCache):
def _load(self):
print 'loading cache'
data_file = open(settings.TASTYPIE_JSON_CACHE, 'r')
return json.load(data_file)
def _save(self, data):
print 'saving to cache'
data_file = open(settings.TASTYPIE_JSON_CACHE, 'w')
return json.dump(data, data_file)
def get(self, key):
print 'jsoncache.get'
data = self._load()
return data.get(key, None)
def set(self, key, value, timeout=60):
print 'jsoncache.set'
data = self._load()
data[key] = value
self._save(data)
class PollResource(ModelResource):
class Meta:
queryset = Poll.objects.all()
resource_name = 'poll'
cache = JSONCache()
It seems that Tastypie doesn't automatically cache lists, tastypie.resources around line 1027:
def get_list(self, request, **kwargs):
# ...
# TODO: Uncached for now. Invalidation that works for everyone may be
# impossible.
objects = self.obj_get_list(
request=request, **self.remove_api_resource_names(kwargs))
# ...
, whereas with details (around line 1050):
def get_detail(self, request, **kwargs):
# ...
try:
obj = self.cached_obj_get(
request=request, **self.remove_api_resource_names(kwargs))
# ...
... note that in the former snippet obj_get_list is called instead of cached_obj_get_list. Perhaps overriding get_list and using cached_obj_get_list would allow you to use cache here as well?
Now you probably would get output from your class for http://localhost:8000/api/poll/<pk>/?format=json (detail view) but not for http://localhost:8000/api/poll/?format=json (list view) by default.

Categories