I would like to create a datastore model for a book review webapp. This webapp would have two kinds - Book and Review:
class Book(ndb.Model):
title = ndb.StringProperty()
class Review(ndb.Model):
review = ndb.TextProperty()
The Reviewentities should be child entities to the parenting Book entities. Upon viewing a page of a Book entity, its list of Review child entities should also be queried and shown like this:
Review.query(ancestor=book.key).fetch()
The (simplified) handlers below would handle the creation of new entities:
class NewBook(BaseHandler):
def get(self):
self.render('new-book-form.html')
def post(self):
title = self.request.get('title')
b = Book(title = title)
b.put()
class NewReview(BaseHandler):
def get(self):
self.render('new-review-form.html')
def post(self):
book_key = self.get_book_key() # gets book entity's key
review = self.request.get('review')
r = Review(review = review) # Do I define this entity's parent here?
r.put()
According to the documentation, a parent is assigned inside a child entity's key like this:
review = Review(parent=ndbKey(Book, book_id), review = review)
At which point inside the NewReview handler do I define this? I may be missing something very obvious.
You pretty much have it right. Assuming your get_book_key method returns an actual ndb.Key instance, the line you've commented becomes:
r = Review(parent=book_key, review=review)
Related
I'm writing a web scraper to get information about customers and appointment times to visit them. I have a class called Job that stores all the details about a specific job. (Some of its attributes are custom classes too e.g Client).
class Job:
def __init__(self, id_=None, client=Client(None), appointment=Appointment(address=Address(None)), folder=None,
notes=None, specific_reqs=None, system_notes=None):
self.id = id_
self.client = client
self.appointment = appointment
self.notes = notes
self.folder = folder
self.specific_reqs = specific_reqs
self.system_notes = system_notes
def set_appointment_date(self, time, time_format):
pass
def set_appointment_address(self, address, postcode):
pass
def __str__(self):
pass
My scraper works great as a stand alone app producing one instance of Job for each page of data scraped.
I now want to save these instances to a Django database.
I know I need to create a model to map the Job class onto but that's where I get lost.
From the Django docs (https://docs.djangoproject.com/en2.1/howto/custom-model-fields/) it says in order to use my Job class in the Django model I don't have to change it at all. That's great - just what I want. but I can't follow how to create a model that maps to my Job class.
Should it be something like
from django.db import models
import Job ,Client
class JobField(models.Field):
description = "Job details"
def __init__(self, *args, **kwargs):
kwargs['id_'] = Job.id_
kwargs['client'] = Client(name=name)
...
super().__init__(*args, **kwargs)
class Job(models.Model):
job = JobField()
And then I'd create a job using something like
Job.objects.create(id_=10101, name="Joe bloggs")
What I really want to know is am I on the right lines? Or (more likely) how wrong is this approach?
I know there must be a big chunk of something missing here but I can't work out what.
By mapping I'm assuming you want to automatically generate a Django model that can be migrated in the database, and theoretically that is possible if you know what field types you have, and from that code you don't really have that information.
What you need to do is to define a Django model like exemplified in https://docs.djangoproject.com/en/2.1/topics/db/models/.
Basically you have to create in a project app's models.py the following class:
from django import models
class Job(models.Model):
client = models.ForeignKey(to=SomeClientModel)
appointment = models.DateTimeField()
notes = models.CharField(max_length=250)
folder = models.CharField(max_length=250)
specific_reqs = models.CharField(max_length=250)
system_notes = models.CharField(max_length=250)
I don't know what data types you actually have there, you'll have to figure that out yourself and cross-reference it to https://docs.djangoproject.com/en/2.1/ref/models/fields/#model-field-types. This was just an example for you to understand how to define it.
After you have these figured out you can do the Job.objects.create(...yourdata).
You don't need to add an id field, because Django creates one by default for all models.
I'm trying to save a datastore entity reference within another:
class Save(webapp2.RequestHandler):
def get(self):
order = Order(parent=ndb.Key('Orders', 'default_orders'))
order.special_request = self.request.get('specialRequirement')
order.product_type = self.request.get('productType')
customer = Customer(parent=ndb.Key('Customer', 'default_customers'))
customer.name = self.request.get('customerName')
customer.email = self.request.get('email')
customer.put()
order.customer = customer
order.put()
The Customer class is simply:
from google.appengine.ext import ndb
class Customer(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty()
Whilst I've done similar with Rails and mongodb before, I'm not sure what this is called in GAE and am having a hard time searching for examples.
Ok, the following seems to have been my oversight, simply passing the key as:
oder.customer = customer.key
I now have a usable reference to the embedded object and both are being saved correctly.
I have a function that gets an image from a form, and put's it into the database along with the username. So, here is my database:
class Imagedb(db.Model):
name = db.StringProperty(required = True)
image = db.BlobProperty()
And here is the code that writes to the database:
class Change_Profile_Image(MainHandler):
def get(self):
if self.user:
self.render('change_profile_image.html', username = self.user.name, firstname=self.user.first_name)
else:
self.render('change_profile_image.html')
def post(self):
imagedb = Imagedb(name = self.user.name)
imageupl = self.request.get("img")
imagedb.image = db.Blob(imageupl)
imagedb.put()
self.redirect('/profile')
Any who, it works awesome. Except for one thing. What i'm trying to accomplish is only storing ONE profile picture. What ends up happening is this:
Say I am the user admin. Ill upload a display pic, that pic shows in the profile. Ill upload another one, that one shows. Cool, except for the fact that I have 2 objects in my database that have the name = admin attribute. I would like to edit this...
def post(self):
imagedb = Imagedb(name = self.user.name)
imageupl = self.request.get("img")
imagedb.image = db.Blob(imageupl)
imagedb.put()
self.redirect('/profile')
so that I can post images to the database, but if one exists, it is overwritten. Could anyone help me with this please? I'm relatively new to python and app engine.
If something is unclear, please let me know.
You want to set the key of the Imagedb entity to "name". Essentially, you don't need the name field, but you'll instantiate it like
imagedb = Imagedb(key_name = self.user.name)
The key is a required field on all entities. By using your user name as the key it means every time you refere to a given key, it's the same entity.
I'm not sure how to title this question. I've also simplified my code so it's easier to ask. Say I have the following code in myproject.models in Pyramid:
class Links(Base):
__tablename__ = 'links'
id = Column(Integer, primary_key=True)
link = Column(Text)
def __init__(self, link):
self.link = link
class Submissions(Base):
__tablename__ = 'submissions'
id = Column(Integer, primary_key=True)
title = Column(Text)
link_id = Column(Integer, ForeignKey('links.id'))
link = relationship(Links)
def __init__(self, title, link):
self.title = title
self.link = link
The view will be very simple:
def my_view(request):
dbsession = DBSession()
submissions = dbsession.query(Submissions)
return {'submissions':submissions}
I want to return this on my page using Chameleon:
<p tal:repeat="thing submissions">
${thing.title} ${thing.link}
</p>
However, ${thing.link} doesn't show the link of the site.
Questions:
How do I reference thing.link's link? Intuitively, I would type ${thing.link.link}, but that doesn't work.
How do I reference an arbitrary subclass? I want to be able to extract any attribute from an object's subclass, for example, thing.link.link, thing.link.domain, thing.link.created, etc.
BTW, someone please tell me a better title to give this question.
In your example, you are missing the .all() after your .query(). You can check in your view if your submissions are really loaded by doing something like
for submission in submissions:
print submission.id, submission.title
and then watch your console when loading the page.
Then, when you confirmed you really have them loaded, you can access the link object with submission.link. In the link object, you can access the link attribute with .link.
for submission in submissions:
print submission.link.link
So in your template, you could write ${thing.link.link}.
Assuming you do have the link object attached (given the fact that the link_id column is not nullable), most probably you need to (eager)load the relationship to Links because the session is alread closed when you populate your view.
See Relationship Loading Techniques for more information. The code below should do it:
submissions = dbsession.query(Submissions).options(joinedload('link'))
In the past, I created a Django wiki, and it was fairly straightforward to make a Page table for the current wiki entries, and then to store old revisions into a Revision table.
More recently, I decided to set up a website on Google App Engine, and I used some wiki code that another programmer wrote. Because he created his Page model in sort of a complicated way (complicated to me at least) using Entities, I am unsure about how to create the Revision table and integrate it with his Page model.
Here is the relevant code. Could someone help me write the Revision model, and integrate saving the revisions with the Save method of the Page model?
class Page(object):
def __init__(self, name, entity=None):
self.name = name
self.entity = entity
if entity:
self.content = entity['content']
if entity.has_key('user'):
self.user = entity['user']
else:
self.user = None
self.created = entity['created']
self.modified = entity['modified']
else:
# New pages should start out with a simple title to get the user going
now = datetime.datetime.now()
self.content = '<h1>' + cgi.escape(name) + '</h1>'
self.user = None
self.created = now
self.modified = now
def save(self):
"""Creates or edits this page in the datastore."""
now = datetime.datetime.now()
if self.entity:
entity = self.entity
else:
entity = datastore.Entity('Page')
entity['name'] = self.name
entity['created'] = now
entity['content'] = datastore_types.Text(self.content)
entity['modified'] = now
if users.GetCurrentUser():
entity['user'] = users.GetCurrentUser()
elif entity.has_key('user'):
del entity['user']
datastore.Put(entity)
By the way, this code comes from: http://code.google.com/p/google-app-engine-samples/downloads/list
I'm pretty inexperienced with GAE Django models, and mine tend to be very simple. For example, here's my model for a blog Article:
class Article(db.Model):
author = db.UserProperty()
title = db.StringProperty(required=True)
text = db.TextProperty(required=True)
tags = db.StringProperty(required=True)
date_created = db.DateProperty(auto_now_add=True)
The code in your first snippet is not a model - it's a custom class that uses the low-level datastore module. If you want to extend it, I would recommend throwing it out and replacing it with actual models, along similar lines to the Article model you demonstrated in your second snippet.
Also, they're App Engine models, not Django models - Django models don't work on App Engine.
I created this model (which mimics the Page class):
class Revision (db.Model):
name = db.StringProperty(required=True)
created = db.DateTimeProperty(required=True)
modified = db.DateTimeProperty(auto_now_add=True)
content = db.TextProperty(required=True)
user = db.UserProperty()
In the Save() method of the Page class, I added this code to save a Revision, before I updated the fields with the new data:
r = Revision(name = self.name,
content = self.content,
created = self.created,
modified = self.modified,
user = self.user)
r.put()
I have the wiki set up now to accept a GET parameter to specify which revision you want to see or edit. When the user wants a revision, I fetch the Page from the database, and replace the Page's Content with the Revision's Content:
page = models.Page.load(title)
if request.GET.get('rev'):
query = db.Query(models.Revision)
query.filter('name =', title).order('created')
rev = request.GET.get('rev')
rev_page = query.fetch(1, int(rev))
page.content = rev_page.content