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!
Related
I am using a class based service in python and I get error whenever I want to use it. Unable to figure out the reason.
#!/usr/bin/python
# -*- coding: utf-8 -*-
from xml.dom import minidom
from pysimplesoap.client import SoapClient
from pysimplesoap.helpers import sort_dict
MEDIA_ROOT = '/User/sunand/documents/resumes/'
parser = ResumeParser()
names = parser.get_names(MEDIA_ROOT)
print names
class ParserClient(SoapClient):
""" Extends the soap client to encode the response with utf-8 encoding.
"""
def wsdl_call(
self,
method,
*args,
**kwargs
):
""" Override wsdl_call method to make sure unmarshall is not called.
"""
operation = self.get_operation(method)
# get i/o type declarations:
inp = operation['input']
header = operation.get('header')
if 'action' in operation:
self.action = operation['action']
# construct header and parameters
if header:
self.__call_headers = sort_dict(header, self.__headers)
(method, params) = self.wsdl_call_get_params(method, inp,
*args, **kwargs)
response = self.call(method, *params)
return response
def send(self, method, xml):
""" Overrides the send method to get the actual xml content.
"""
content = super(ParserClient, self).send(method, xml)
self.result = content
return content
class ResumeParser(object):
""" Connects to the Resume Parser's XML api to get parsed data.
"""
def __init__(self, simple=True, timeout=60):
""" Initializes the ResumeParser class.
"""
self.wsdl = \
'http://jobsite.onlineresumeparser.com/rPlusParseResume.asmx?WSDL'
self.secret = 'my-secret-key' # Enter key here
self.encoding = 'base64'
self.simple = simple
self.client = ParserClient(wsdl=self.wsdl, timeout=timeout)
self.names = []
def get_file_content(self, file_path):
""" Return the encoded content for the given file.
"""
file_obj = open(os.path.abspath(file_path), 'r')
content = file_obj.read().encode(self.encoding)
file_obj.close()
return content
def get_names(self, path):
"""
Given a path to a folder that contains resume files this method
will parse the resumes and will return the names of the candidates
as a list.
"""
opt = os.path
resumes = [opt.join(path, r) for r in os.listdir(path)
if opt.isfile(opt.join(path, r))]
# Parse information for each resume.
for resume in resumes:
try:
xml_data = self.get_xml(resume)
name = self.get_name_from_xml(xml_data)
if name:
self.names.append(name)
except Exception, err:
# print name
print 'Error parsing resume: %s' % str(err)
return list(set(self.names))
def get_name_from_xml(self, data):
""" Returns the full name from the xml data given.
"""
xmldata = minidom.parseString(data)
name = xmldata.getElementsByTagName('CANDIDATE_FULL_NAME')
name = name[0].childNodes[0].data.title()
return name
def get_xml(self, filepath):
""" Fetches and returns the xml for the given file from the api.
"""
filename = os.path.basename(filepath)
extension = os.path.splitext(filepath)[1]
base64 = self.get_file_content(filepath)
filedata = {
'B64FileZippedContent': base64,
'FileName': filename,
'InputType': extension,
'UserID': 1,
'secretKey': self.secret,
}
get = \
(self.client.GetSimpleXML if self.simple else self.client.getHRXML)
get(**filedata)
return self.process_raw_xml()
def process_raw_xml(self, data=None):
""" Processes and returns the clean XML.
"""
raw = (data if data else self.client.result)
parsed = minidom.parseString(raw)
result = parsed.getElementsByTagName('GetSimpleXMLResult')[0]
text_node = result.childNodes[0]
data = text_node.data.encode('UTF-8')
return data
Upon running the code I am getting an error
TypeError: wsdl_call_get_params() got an unexpected keyword argument 'secretKey'
What am I doing wrong?
It looks like you are incorrectly overriding wsdl_call.
Firstly, we can see that SoapClient (which you extend in ParserClient), has a __getattr__ function that fetches pseudo-attributes of the SoapClient.
def __getattr__(self, attr):
"Return a pseudo-method that can be called"
if not self.services: # not using WSDL?
return lambda self=self, *args, **kwargs: self.call(attr,*args,**kwargs)
else: # using WSDL:
return lambda *args, **kwargs: self.wsdl_call(attr,*args,**kwargs)
You can see that this function is using wsdl_call to help it map functions to unknown attributes.
The specific pseudo-method that is causing the problem is in your code (or appears to be):
filedata = {
'B64FileZippedContent': base64,
'FileName': filename,
'InputType': extension,
'UserID': 1,
'secretKey': self.secret, # <-- the secretKey key word argument
}
get = \
(self.client.GetSimpleXML if self.simple else self.client.getHRXML)
get(**filedata)
# here client is an instance of your `ParserClient` (and `SoapClient`).
This above bit took me a while to track down. With a full stack trace I would have found it much quicker. Please always post stack traces (when there is one) in future when asking for help.
How to solve this
Provide a concrete implementation of GetSimpleXML and getHRXML. This will solve the immediate problem, but not the larger problem.
Rewrite wsdl_call
The rewritten section of code should check the value of the method argument and either do what you want, or delegate to the SoapClient implementation.
eg.
def wsdl_call(self, method, *args, **kwargs):
if method == "some_method":
return self._my_wsdl_call(method, *args, **kwargs)
else:
return super(ParserClient, self).wsdl_call(method, *args, **kwargs)
def _my_wsdl_call(self, method, *args, **kwargs):
...
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').
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']
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.
I am having a hard time with tests in Django and Python, for my final project I am making a forums website, but I don't really have any idea how or what my tests should be. Here is the views page from mysite file. Could someone please walk me through what I should test for besides if a user is logged in.
from django.core.urlresolvers import reverse
from settings import MEDIA_ROOT, MEDIA_URL
from django.shortcuts import redirect, render_to_response
from django.template import loader, Context, RequestContext
from mysite2.forum.models import *
def list_forums(request):
"""Main listing."""
forums = Forum.objects.all()
return render_to_response("forum/list_forums.html", {"forums":forums}, context_instance=RequestContext(request))
def mk_paginator(request, items, num_items):
"""Create and return a paginator."""
paginator = Paginator(items, num_items)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
items = paginator.page(page)
except (InvalidPage, EmptyPage):
items = paginator.page(paginator.num_pages)
return items
def list_threads(request, forum_slug):
"""Listing of threads in a forum."""
threads = Thread.objects.filter(forum__slug=forum_slug).order_by("-created")
threads = mk_paginator(request, threads, 20)
template_data = {'threads': threads}
return render_to_response("forum/list_threads.html", template_data, context_instance=RequestContext(request))
def list_posts(request, forum_slug, thread_slug):
"""Listing of posts in a thread."""
posts = Post.objects.filter(thread__slug=thread_slug, thread__forum__slug=forum_slug).order_by("created")
posts = mk_paginator(request, posts, 15)
thread = Thread.objects.get(slug=thread_slug)
template_data = {'posts': posts, 'thread' : thread}
return render_to_response("forum/list_posts.html", template_data, context_instance=RequestContext(request))
def post(request, ptype, pk):
"""Display a post form."""
action = reverse("mysite2.forum.views.%s" % ptype, args=[pk])
if ptype == "new_thread":
title = "Start New Topic"
subject = ''
elif ptype == "reply":
title = "Reply"
subject = "Re: " + Thread.objects.get(pk=pk).title
template_data = {'action': action, 'title' : title, 'subject' : subject}
return render_to_response("forum/post.html", template_data, context_instance=RequestContext(request))
def new_thread(request, pk):
"""Start a new thread."""
p = request.POST
if p["subject"] and p["body"]:
forum = Forum.objects.get(pk=pk)
thread = Thread.objects.create(forum=forum, title=p["subject"], creator=request.user)
Post.objects.create(thread=thread, title=p["subject"], body=p["body"], creator=request.user)
return HttpResponseRedirect(reverse("dbe.forum.views.forum", args=[pk]))
def reply(request, pk):
"""Reply to a thread."""
p = request.POST
if p["body"]:
thread = Thread.objects.get(pk=pk)
post = Post.objects.create(thread=thread, title=p["subject"], body=p["body"],
creator=request.user)
return HttpResponseRedirect(reverse("dbe.forum.views.thread", args=[pk]) + "?page=last")
First read the Django testing documentation. You might also want to read this book. It's dated in some areas, but testing is still pretty much the same now as it was in 1.1.
It's a bit much of a topic to cover in an SO answer.
Well, you could test:
If you have the right number of pages for the objects you're paginating.
If the page you're viewing contains the right object range. If trying to access a page that doesn't exist returns the
appropriate error.
If your views for listing objects and object detail return the correct HTTP status code (200)
For starters. Hope that helps you out.