I just upgraded to Pylons 1.0 and SqlAlchemy 0.6.5. What was a simple process of creating the DB schema no longer works.
I have a simple model:
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
user_name = Column(String)
def __init__(self, userName):
self.userName = userName
def __repr__(self):
return "<User('%s')>" % (self.userName)
When I run
paster setup-app development.ini
the database file is created (sqlite3), but not the table, and no errors are returned.
Logging shows that the following lines in websetup.py do execute:
log.info("Creating schema...")
Base.metadata.create_all(bind = Session.bind, checkfirst = True)
log.info("Database successfully set up.")
What am I missing?
Edit: Further digging shows that the Base.metadata.tables dictionary is empty. So, why isn't the model reflected in the metadata?
Ok, I found the issue.
In model.meta.py, Base = declarative_base() was already performed. When I also added that statement to the model.__init__.py, it evidently creates a new instance without the metadata, so there were no tables to create.
I'm not clear on exactly why/how this works, so if anyone (Mike Bayer?) has details, I would love to know.
Related
I am trying to get the currently logged in User and save it to a model. I am getting the following error
"Cannot assign "<SimpleLazyObject: <User: rsomani005>>": "process_master.user" must be a "User" instance"
I am using Django's built in user model.
My model looks like this -
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return "#{}".format(self.username)
class plan_master(models.Model):
plan_name = models.CharField(max_length=256)
plan_description = models.CharField(max_length=256)
plan_price = models.FloatField()
plan_active_status = models.BooleanField(default=True)
plan_credits = models.IntegerField(default=0)
registration_type_name = models.CharField(max_length=50)
My view file looks like this -
class FileFieldView(LoginRequiredMixin,FormView):
form_class = FileFieldForm
template_name = 'merge_ops/merge_ops.html'
success_url = reverse_lazy('merge_ops:file_setup')
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
fs = FileSystemStorage()
fs.save(f.name,f)
obj = process_master()
user = request.user
obj.user = user
obj.process_date = datetime.now()
obj.number_of_workbooks = len(files)
obj.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
An easy way to get the logged user in one of your views is:
user = request.user
but that is usually when the user model that you have created is something like that in the model.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
pass
I would suggest using this one to register users because it is automatic and complete.
and from there use the object for what you need. You can pass it on as a context or
source model objects from it.
Is it possible to extend the Controlpanel-View with each addon?
For Example
ca.db.core -> Makes basic fieldset/tab for DB Connection Settings
ca.db.person -> If installed, Adds to the "core" settings a new fieldset/tab for Person specific fields/settings
ca.db.schema -> If installed, also adds an new fieldset/tab for schema.org Fields
Yes it's possible, I once discussed this problem with a guy, who wrote the bda.plone.shop addon.
They faced the same problem, and solved it by using a ContextProxy object, which puts the different schema definitions together in one proxy object.
Using a proxy is IMHO a hack, but I don't know a better solution.
The proxy, tries to get/set a attribute from a list of schemas.
Be aware, you need to handle conflicting names, means if you have the same field name in more than one schema.
class ContextProxy(object):
def __init__(self, interfaces):
self.__interfaces = interfaces
alsoProvides(self, *interfaces)
def __setattr__(self, name, value):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__setattr__(self, name, value)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
getattr(proxy, name)
except AttributeError:
pass
else:
return setattr(proxy, name, value)
raise AttributeError(name)
def __getattr__(self, name):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__getattr__(self, name)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
return getattr(proxy, name)
except AttributeError:
pass
raise AttributeError(name)
Now you need to use the proxy in your ControlPanel form.
I assume you're using the RegistryEditForm from plone.registry:
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = ISettings
label = _(u"Settings")
description = _(u"")
# IMPORTANT Note 1 - This is where you hook in your proxy
def getContent(self):
interfaces = [self.schema] # Base schema from ca.db.core
interfaces.extend(self.additionalSchemata) # List of additional schemas
return ContextProxy(interfaces)
# IMPORTANT Note 2 - You may get the additional schemas dynamically to extend the Settings Form. For example by name (startswith...)
# In this case they have a separate interface, which marks the relevant interfaces.
#property
def additionalSchemata(self):
registry = getUtility(IRegistry)
interface_names = set(record.interfaceName for record
in registry.records.values())
for name in interface_names:
if not name:
continue
interface = None
try:
interface = resolve(name)
except ImportError:
# In case of leftover registry entries of uninstalled Products
continue
if ISettingsProvider.providedBy(interface):
yield interface
...
You can find the full code here
I was using the google datastore and jinja2 for starting off. I am able to add and retrieve string values but when I use the email property as :
email=db.Email
and retrieve it using .email, I get
class 'google.appengine.api.datastore_types.Email' from the datastore.
How do i get the value of the email instead?
using .email works for me.
python code
import webapp2
from google.appengine.ext import db
class Greeting(db.Model):
author = db.StringProperty()
email = db.EmailProperty()
class MainPage(webapp2.RequestHandler):
def get(self):
en = Greeting(author='hellooo', email=db.Email("a#a.com"))
en.put()
app = webapp2.WSGIApplication([('/', MainPage)],
debug=True)
and get the value like this
dev~devchat> x = Greeting.get_by_id(2)
dev~devchat> x.author
u'hellooo'
dev~devchat> x.email
u'a#a.com'
dev~devchat> x.email.ToXml()
u'<gd:email address="a#a.com" />'
in a Plone add-on product, I have a control panel page where some config options can be set. They are stored in plone.registry. The control panel adapter fetches the different fields in its __init__ method by querying the interface, like:
class MultiLanguageExtraOptionsAdapter(LanguageControlPanelAdapter):
implementsOnly(IMultiLanguageExtraOptionsSchema)
def __init__(self, context):
super(MultiLanguageExtraOptionsAdapter, self).__init__(context)
self.registry = getUtility(IRegistry)
self.settings = self.registry.forInterface(
IMultiLanguageExtraOptionsSchema)
Now I add an additional field to the interface IMultiLanguageExtraOptionsSchema and restart plone. On the control panel page I then an error:
KeyError: 'Interface `plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema` defines a field `blah`, for which there is no record.'
(This is expected for the forInterfacemethod , as described on the plone.registry README. The record is not there.)
Of course, if I add that field via GenericSetup (registry.xml), and I re-install the product / re-run the "Control Panel" step, all is well:
<registry>
<records interface="plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema">
<value key="blah"></value>
<records>
<registry>
But I don't want to force users to re-install a product, just because there's a new option in the product-specific control panel. So my question: Is there a recommended way for getting a new record for a new field into plone.registry?
You could try/catch the KeyError and then make sure all registry settings are registered:
try:
self.settings = self.registry.forInterface(IMultiLanguageExtraOptionsSchema)
except KeyError:
registry = getUtility(IRegistry)
registry.registerInterface(IMultiLanguageExtraOptionsSchema)
I would recommend to write an upgrade step though (which would force your users to reinstall the product of course).
upgrades.py:
def update_registry(context):
registry = getUtility(IRegistry)
registry.registerInterface(IMultiLanguageExtraOptionsSchema)
upgrades.zcml::
<genericsetup:upgradeStep
source="*"
destination="1100"
title="Update plone.app.multilingual setting registry"
description=""
profile="plone.app.multilingual:default"
handler=".upgrades.update_registry"
/>
See
https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.py
and
https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.zcml
for an example.
If you pass False as the second parameter to forInterface:
registry.forInterface(IMultiLanguageExtraOptionsSchema, False)
then it won't throw an error if fields from the schema are missing from the registry, but will simply return the field's default value.
Safe getting settings from registry:
def get_registry_settings(interface, name):
registry = getUtility(IRegistry)
settings = registry.forInterface(interface, check=False)
value = getattr(settings, name)
if value == settings.__schema__[name].missing_value:
value = settings.__schema__[name].default
return value
How to get a session id in webapp2?
It doesn't seem to be documented anywhere and it wasn't trivial to me.
I have found some solution, which I present in the answer to this question, but maybe somebody will find simpler or better one.
Anyway I think that this can be useful to somebody
My solution looks like this:
import webapp2
from webapp2_extras import sessions
class Test(webapp2.RequestHandler):
def get(self):
session_store = sessions.get_store(request=self.request)
cookie_name = session_store.config['cookie_name']
session_id = self.request.cookies[cookie_name]
self.response.out.write('<html><body>')
self.response.out.write('Session id: %s' % session_id)
self.response.out.write("</body></html>")
In your example, you can't get the session id, until you refresh the page. You should make an authenticate page to create the session. Also you should define a base handler that extends the dispatch() method to start the session store and save all sessions at the end of a request. Read the documentation See an example:
class Main(main.BaseHandler):
def get(self):
if not self.session.get('user'):
self.redirect("/authenticate",abort=True)
session = self.request.cookies("session")
#more stuff........
class Authenticate(main.BaseHandler):
def get(self):
if self.session.get('user'):
self.redirect("/")
else:
self.render("authenticate.htm")
def post(self):
if self.session.get('user'):
self.redirect("/",abort=True)
post_param = {"username":self.request.get("username"),
"password":self.request.get("password")}
if not post_param["password"] or not post_param["username"]:
redirect("/authenticate")
mySql = dbLib.MySqlLib()
try:
mySql.query("SELECT password FROM users where username=%s", post_param["username"],))
dbPassword = mySql.dbCur.fetchone()[0]
except dbLib.ProgrmmgError as error:
self.render("authenticate.htm",username = post_param["username"],
errorMessage = "Password and/or Username\
else: invalid")
if bcrypt.hashpw(post_param["password"],dbPassword) == dbPassword:
self.session['user'] = "root"
self.redirect("/")
else:
self.render("authenticate.htm",username = post_param["username"],
errorMessage = "Password and/or Username\
mySql.close()
app = main.webapp2.WSGIApplication([('/', Main),
("/authenticate",Authenticate),
(".*",main.Error_404)],
debug=True)