Hi all as i in learning stage of django so support me.
I have to generate pdf reports in django.I want that the details should be picked from the database and displayed in the pdf document.i am using report lab.
Now have a look at the code
def pdf_view(request):
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=hello.pdf'
p = canvas.Canvas(response)
details = Data.objects.all()
print details
p.drawString(20, 800, details)
p.drawString(30, 700, "I am a Python Django Professional.")
p.showPage()
p.save()
return response
now as a learning example i have made two fields in models
class Data(models.Model):
first_name = models.CharField(max_length =100,blank=True,null=True)
last_name = models.CharField(max_length =100,blank=True,null=True)
def __unicode__(self):
return self.first_name
and i want that in the pdf document it should display the name s whatever i fill through admin but it is giving me error
'Data' object has no attribute 'decode'
Request Method: GET
Request URL: http://localhost:8000/view_pdf/
Django Version: 1.3
Exception Type: AttributeError
Exception Value:
i want to pik the details from the database and display in the pdf document
'Data' object has no attribute 'decode'
It would have helped if you'd posted the actual traceback.
However I expect the issue is this line:
p.drawString(20, 800, details)
Details is a queryset, that is a list-like container of model instances. It's not a string, and neither does it contain a string. Maybe you want something like:
detail_string = u", ".join(unicode(obj) for obj in details)
which calls the __unicode__ method on every object in your queryset, and joins the resulting list with commas.
Related
I am creating a view function to edit the database using a wtform, I want to populate the form with information held on the database supplied by a differente form, My problem is the query that provides the details
I have read the manual https://wtforms.readthedocs.io/en/stable/crash_course.html
and the following question Python Flask-WTF - use same form template for add and edit operations
but my query does not seem to supply the correct format of data
datatbase model:
class Sensors(db.Model):
id = db.Column(db.Integer, primary_key=True)
sensorID = db.Column(db.String, unique=True)
name = db.Column(db.String(30), unique=True)
form model:
class AddSensorForm(FlaskForm):
sensorID = StringField('sensorID', validators=[DataRequired()])
sensorName = StringField('sensorName', validators=[DataRequired()])
submit = SubmitField('Register')
view function:
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.\
query(Sensors).filter_by(id=id).all()]
form = AddSensorForm(obj=edit)
form.populate_obj(edit)
if form.validate_on_submit():
sensors = Sensors(sensorID=form.sensorID.data, sensorName=form.sensorNa$
db.session.add(sensors)
db.session.commit()
shell code for query:
from homeHeating import db
from homeHeating import create_app
app = create_app()
app.app_context().push()
def editsensor(id):
edit = [(s.sensorID, s.sensorName) for s in db.session.query(Sensors).filter_by(id=id).all()]
print(edit)
editsensor(1)
[('28-0000045680fde', 'Boiler input')]
I expect that the two form fields will be populated with the in formation concerning the sensor called by its 'id'
but I get this error
File "/home/pi/heating/homeHeating/sensors/sensors.py", line 60, in
editsensor
form.populate_obj(edit)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/form.py", line 96, in populate_obj
Open an interactive python shell in this
framefield.populate_obj(obj, name)
File "/home/pi/heating/venv/lib/python3.7/site-
packages/wtforms/fields/core.py", line 330, in populate_obj
setattr(obj, name, self.data)
AttributeError: 'list' object has no attribute 'sensorID'
The error indicates that it wants 2 parts for each field "framefield.populate_obj(obj, name) mine provides only one the column data but not the column name, "sensorID"
If i hash # out the line "edit = ..." then there are no error messages and the form is returned but the fields are empty. So I want the form to be returned with the information in the database, filled in so that i can modify the name or the sensorID and then update the database.
I hope that this is clear
Warm regards
paul.
ps I have followed the instruction so the ERROR statement is only the part after "field.populate_by".
You are trying to pass a 1-item list to your form.
Typically, when you are selecting a single record based on the primary key of your model, use Query.get() instead of Query.filter(...).all()[0].
Furthermore, you need to pass the request data to your form to validate it on submit, and also to pre-fill the fields when the form reports errors.
Form.validate_on_submit will be return True only if your request method is POST and your form passes validation; it is the step where your form tells you "the user provided syntactically correct information, now you may do more checks and I may populate an existing object with the data provided to me".
You also need to handle cases where the form is being displayed to the user for the first time.
#bp.route('/sensors/editsensor/<int:id>', methods=('GET', 'POST'))
#login_required
def editsensor(id):
obj = Sensors.query.get(id) or Sensors()
form = AddSensorForm(request.form, obj=obj)
if form.validate_on_submit():
form.populate_obj(obj)
db.session.add(obj)
db.session.commit()
# return response or redirect here
return redirect(...)
else:
# either the form has errors, or the user is displaying it for
# the first time (GET)
return render_template('sensors.html', form=form, obj=obj)
I would like to test a file upload from my view with the following function:
def test_post(self):
with open("path/to/myfile/test_file.txt") as file:
post_data = {
'description': "Some important file",
'file': file,
}
response = self.client.post(self.test_url, post_data)
self.assertEqual(response.status_code, 302)
document = Document.objects.first()
self.assertEqual(document.description, "My File")
self.assertEqual(document.filename, 'test_file.txt')
When I test the file upload on the actual website, it works. But when I run this test, I get the following error:
django.core.exceptions.SuspiciousFileOperation: Storage can not find
an available filename for "WHJpMYuGGCMdSKFruieo/Documents/eWEjGvEojETTghSVCijsaCINZVxTvzpXNRBvmpjOrYvKAKCjYu/test_file_KTEMuQa.txt".
Please make sure that the corresponding file field allows sufficient
"max_length".
Here is my form save method:
def save(self, commit=True):
instance = super(DocumentForm, self).save(commit=False)
instance.filename = self.cleaned_data['file'].name
if commit:
instance.save() # error occurs here
return instance
Seeing as it works on the actual website, I suspect it has something to do with the way I setup the file in the test; probably something small.
For the sake of brevity, I had removed irrelevant model fields from my original question. But when Ahtisham requested to see the upload_to attribute (which had a custom function), I removed those irrelevant fields and it worked!
So this is my original code (which didn't work) with the irrelevant fields:
def documents_path(instance, filename):
grant = instance.grant
client = grant.client
return '{0}/Grant Documents/{1}/{2}'.format(client.folder_name, grant.name, filename)
....
file = models.FileField(upload_to=documents_path)
But this works:
def documents_path(instance, filename):
return 'Documents/{0}'.format(filename)
The reason why it worked on the actual website is because it wasn't using long characters from the test fixtures. Seems like those fields that I thought were irrelevant for the question were actually quite important!
TL;DR I decreased the length of the custom document path.
I had the same error with an ImageField. The solution was adding max_length=255 in models.py:
image = models.ImageField(upload_to=user_path, max_length=255)
Then running python manage.py makemigrations and python manage.py migrate.
AIM
Once the User enters search_text in the HashtagSearch FormView, the function get_tweets() will get from Twitter the locations associated with that hashtag.
Then, use get_or_create to save the search_text in the Hashtag db. Then, go through the txt file line by line and, assuming the regex requirements have been satisfied, add the line as a locations associated with the search_text in the db.
As a summary of the workflow:
A User enters search_text in the HashtagSearch FormView,
A function is run within HashtagSearch FormView that searches Twitter for tweets using the search_text as a hashtag ('the applicable tweets'),
Searches 'the applicable tweets' for whether the tweeter has a location saved on their profile. If so, save that location to a txt file,
Performs regex on the txt file to filter out 'non-genuine' locations,
Saves the locations to the locations object associated with the search_text in the Hashtag model.
Render results.html with the location object associated with the search_text in the Hashtag model.
ERROR
Request Method: POST
Django Version: 2.0
Exception Type: AttributeError
Exception Value: 'tuple' object has no attribute 'locations'
Exception Location: /mnt/project/mapping_twitter/views.py in get_tweets, line 87
Python Executable: /mnt/data/.python-3.6/bin/python
Python Version: 3.6.5
Python Path:
['/mnt/project',
'/mnt/data/.python-3.6/lib/python36.zip',
'/mnt/data/.python-3.6/lib/python3.6',
'/mnt/data/.python-3.6/lib/python3.6/lib-dynload',
'/usr/local/lib/python3.6',
'/mnt/data/.python-3.6/lib/python3.6/site-packages']
CODE
views.py
def get_tweets(self, form):
""" Get tweets from the Twitter API and store them to the db """
consumer_key = '...'
consumer_secret = '...'
access_token = '...'
access_secret = '...'
t = Twython(
app_key=consumer_key,
app_secret=consumer_secret,
oauth_token=access_token,
oauth_token_secret=access_secret,
)
# set to the text entered by User in Form
search_filter = self.request.POST.get('search_text')
# set the filter for hashtag and quantity
search = t.search(q='#{}'.format(search_filter), count=50)
tweets = search['statuses']
f = open('tweet_locations.txt', 'w+')
for tweet in tweets:
if tweet['user']['location']:
tweet_location = tweet['user']['location']
f.write("{}\n".format(tweet_location))
f.close()
data = open('tweet_locations.txt', 'r')
# Regex out 'unmappable' locations
valid_ex = re.compile(r'[A-Z][a-z]+, [A-Za-z]+')
for line in data:
valid_tweet_location = str(valid_ex.search(line))
if valid_tweet_location:
# ISSUE: save the locations_list to the Hashtag model as the locations associated with the search_text entered
tweet_object = Hashtag.objects.get_or_create(search_text=search_filter)
# necessary as locations is a M2M object
tweet_object.locations.create(valid_tweet_location)
data.close()
The get_or_create method returns the tuple (obj, created) instead of only the object. This way you can check whether the object is retrieved or created.
Simply unpack the tuple as follows:
tweet_object, created = Hashtag.objects.get_or_create(search_text=search_filter)
tweet_object.locations.create(valid_tweet_location)
You posted far too much code here - the traceback showed you where the error was happening, you should have just posted the get_tweets method.
The error is indeed happening there. This is because a tuple is what is returned by get_or_create - that is, it returns the object and a boolean showing if it was a create. Since your don't care about that, you can just assign it to a variable that is ignored:
tweet_object, _ = Hashtag.objects.get_or_create(search_text=search_filter)
I need some help with MongoAlchemy. I'm trying to create a web application with python, flask, Mongo DM and Mongo Alchemy (as an object document mapper) and I'm struggling with updating existing documents.
My problem is that I cannot update an existing document through it's object Id. Below I'm attaching my def for updating
#app.route('/update', methods =[ 'GET', 'POST' ])
def update():
if request.method == 'POST':
id = request.form['object_id'] # Getting values with html
patientzero = BloodDoonor.query.get_or_404(id)
first_name = request.form['name']# Getting values with htmlform
last_name = request.form['surname']# Getting values with html form
blood_type = request.form['blood_type']# Getting values with html
update_record = BloodDoonor.patientzero.update(BloodDoonor.last_name = last_name)
return render_template('update.html', result = result)
And flask gives me that error:
AttributeError AttributeError: type
object 'BloodDoonor' has no attribute 'patientzero'
I'm very new to Python and not very good in code. Please forgive me for the sloppy description I gave above. Any help would be appreciated.
To update an existing document just change the value of the object which you queried from db with form values and then just save that object:
#app.route('/update', methods =[ 'GET', 'POST' ])
def update():
if request.method == 'POST':
id = request.form['object_id']
patientzero = BloodDoonor.query.get_or_404(id)
patientzero.first_name = request.form['name']
patientzero.last_name = request.form['surname']
patientzero.blood_type = request.form['blood_type']
patientzero.save()
return render_template('update.html')
I am trying to create a REST API with Neo4j and Django in the backend.
The problem is that even when I have Django models using Neo4Django , I can't use frameworks like Tastypie or Piston that normally serialize models into JSON (or XML).
Sorry if my question is confusing or not clear, I am newbie to webservices.
Thanks for you help
EDIT: So I started with Tastypie and followed the tutorial on this page http://django-tastypie.readthedocs.org/en/latest/tutorial.html. I am looking for displaying the Neo4j JSON response in the browser, but when I try to access to http://127.0.0.1:8000/api/node/?format=json I get this error instead:
{"error_message": "'NoneType' object is not callable", "traceback": "Traceback (most recent call last):\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 217, in wrapper\n response = callback(request, *args, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 459, in dispatch_list\n return self.dispatch('list', request, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 491, in dispatch\n response = method(request, **kwargs)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 1298, in get_list\n base_bundle = self.build_bundle(request=request)\n\n File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 718, in build_bundle\n obj = self._meta.object_class()\n\nTypeError: 'NoneType' object is not callable\n"}
Here is my code :
api.py file:
class NodeResource (ModelResource): #it doesn't work with Resource neither
class meta:
queryset= Node.objects.all()
resource_name = 'node'
urls.py file:
node_resource= NodeResource()
urlpatterns = patterns('',
url(r'^api/', include(node_resource.urls)),
models.py file :
class Node(models.NodeModel):
p1 = models.StringProperty()
p2 = models.StringProperty()
I would advise steering away from passing Neo4j REST API responses directly through your application. Not only would you not be in control of the structure of these data formats as they evolve and deprecate (which they do) but you would be exposing unnecessary internals of your database layer.
Besides Neo4Django, you have a couple of other options you might want to consider. Neomodel is another model layer designed for Django and intended to act like the built-in ORM; you also have the option of the raw OGM layer provided by py2neo which may help but isn't Django-specific.
It's worth remembering that Django and its plug-ins have been designed around a traditional RDBMS, not a graph database, so none of these solutions will be perfect. Whatever you choose, you're likely to have to carry out a fair amount of transformation work to create your application's API.
Django-Tastypie allows to create REST APIs with NoSQL databases as well as mentioned in http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html.
The principle is to use tastypie.resources.Resource and not tastypie.resources.ModelResource which is SPECIFIC to RDBMS, then main functions must be redefined in order to provide a JSON with the desired parameters.
So I took the example given in the link, modified it and used Neo4j REST Client for Python to get an instance of the db and perform requests, and it worked like a charm.
Thanks for all your responses :)
Thanks to recent contributions, Neo4django now supports Tastypie out of the box! I'd love to know what you think if you try it out.
EDIT:
I've just run through the tastypie tutorial, and posted a gist with the resulting example. I noticed nested resources are a little funny, but otherwise it works great. I'm pretty sure the gents who contributed the patches enabling this support also know how to take care of nested resources- I'll ask them to speak up.
EDIT:
As long as relationships are specified in the ModelResource, they work great. If anyone would like to see examples, let me know.
Well my answer was a bit vague so I'm gonna post how a solved the problem with some code:
Assume that I want to create an airport resource with some attributes. I will structure this in 3 different files (for readability reasons).
First : airport.py
This file will contain all the resource attributes and a constructor too :
from models import *
class Airport(object):
def __init__ (self, iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude):
self.icao = icao
self.iata = iata
self.name = name
self.geonamesId = geonamesId
self.wikipedia = wikipedia
self.id = id
self.latitude = latitude
self.longitude = longitude
self.asciiName = asciiName
This file will be used in order to create resources.
Then the second file : AirportResource.py:
This file will contain the resource attributes and some basic methods depending on which request we want our resource to handle.
class AirportResource(Resource):
iata = fields.CharField(attribute='iata')
icao = fields.CharField(attribute='icao')
name = fields.CharField(attribute='name')
asciiName = fields.CharField(attribute='asciiName')
latitude = fields.FloatField(attribute='latitude')
longitude = fields.FloatField(attribute='longitude')
wikipedia= fields.CharField(attribute='wikipedia')
geonamesId= fields.IntegerField(attribute='geonamesId')
class Meta:
resource_name = 'airport'
object_class = Airport
allowed_methods=['get', 'put']
collection_name = 'airports'
detail_uri_name = 'id'
def detail_uri_kwargs(self, bundle_or_obj):
kwargs = {}
if isinstance(bundle_or_obj, Bundle):
kwargs['id'] = bundle_or_obj.obj.id
else:
kwargs['id'] = bundle_or_obj.id
return kwargs
As mentioned in the docs, if we want to create an API that handle CREATE, GET, PUT, POST and DELETE requests, we must override/implement the following methods :
def obj_get_list(self, bundle, **kwargs) : to GET a list of objects
def obj_get(self, bundle, **kwargs) : to GET an individual object
def obj_create(self, bundle, **kwargs) to create an object (CREATE method)
def obj_update(self, bundle, **kwargs) to update an object (PUT method)
def obj_delete(self, bundle, **kwargs) to delete an object (DELETE method)
(see http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html)
Normally, in ModelResource all those methods are defined and implemented, so they can be used directly without any difficulty. But in this case, they should be customized according to what we want to do.
Let's see an example of implementing obj_get_list and obj_get :
For obj_get_list:
In ModelResource, the data is FIRSTLY fetched from the database, then it could be FILTERED according to the filter declared in META class ( see http://django-tastypie.readthedocs.org/en/latest/interacting.html). But I didn't wish to implement such behavior (get everything then filter), so I made a query to Neo4j given the query string parameters:
def obj_get_list(self,bundle, **kwargs):
data=[]
params= []
for key in bundle.request.GET.iterkeys():
params.append(key)
if "search" in params :
query= bundle.request.GET['search']
try:
results = manager.searchAirport(query)
data = createAirportResources(results)
except Exception as e:
raise NotFound(e)
else:
raise BadRequest("Non valid URL")
return data
and for obj_get:
def obj_get(self, bundle, **kwargs):
id= kwargs['id']
try :
airportNode = manager.getAirportNode(id)
airport = createAirportResources([airportNode])
return airport[0]
except Exception as e :
raise NotFound(e)
and finally a generic function that takes as parameter a list of nodes and returns a list of Airport objects:
def createAirportResources(nodes):
data= []
for node in nodes:
iata = node.properties['iata']
icao = node.properties['icao']
name = node.properties['name']
asciiName = node.properties['asciiName']
geonamesId = node.properties['geonamesId']
wikipedia = node.properties['wikipedia']
id = node.id
latitude = node.properties['latitude']
longitude = node.properties['longitude']
airport = Airport(iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude)
data.append(airport)
return data
Now the third manager.py : which is in charge of making queries to the database and returning results :
First of all, I get an instance of the database using neo4j rest client framework :
from neo4jrestclient.client import *
gdb= GraphDatabase("http://localhost:7474/db/data/")
then the function which gets an airport node :
def getAirportNode(id):
if(getNodeType(id) == type):
n= gdb.nodes.get(id)
return n
else:
raise Exception("This airport doesn't exist in the database")
and the one to perform search (I am using a server plugin, see Neo4j docs for more details):
def searchAirport(query):
airports= gdb.extensions.Search.search(query=query.strip(), searchType='airports', max=6)
if len(airports) == 0:
raise Exception('No airports match your query')
else:
return results
Hope this will help :)