Having some trouble creating my Model in Pylons - python

I've been reading the Pylons Book and, having got to the part about Models, realise it's out of date. So I then switched over to the official Pylons documentation for creating Models in Pylons 1.0 - http://pylonshq.com/docs/en/1.0/tutorials/quickwiki_tutorial/
I've followed what they've got and it's still failing.
./blog/model/init.py
"""The application's model objects"""
from sqlalchemy import orm, Column, Unicode, UnicodeText
from blog.model.meta import Session, Base
def init_model(engine):
"""Call me before using any of the tables or classes in the model"""
Session.configure(bind=engine)
class Page(Base):
__tablename__ = 'pages'
title = Column(Unicode(40), primary_key=True)
content = Column(UnicodeText(), default=u'')
class Page(object):
def __init__(self, title, content=None):
self.title = title
self.content = content
def __unicode__(self):
return self.title
__str__ = __unicode__
orm.mapper(Page, pages_table)
Having two classes with the same name kind of blows my mind... But nevertheless, it's what the tutorial says to do.
When I try to run my code, however, I get:
28, in <module>
orm.mapper(Page, pages_table)
NameError: name 'pages_table' is not defined
Sup with this? How can I get this to not fail? :/

First, you should not declare two classes with same name. How is that supposed to work at all?
Second, you probably would want to read official SQLA docs, not Pylons. Pylons docs are a bit messy after upgrade, and still have a lot of 0.9.7 references.
Declarative extension is described here: http://www.sqlalchemy.org/docs/reference/ext/declarative.html
Third, declarative means you do not need to bind class to table, it is done in the class definition.
This is sufficient declaration of the mapping, you can proceed to using it:
class Page(Base):
__tablename__ = 'pages'
title = Column(Unicode(40), primary_key=True)
content = Column(UnicodeText(), default=u'')
def __init__(self, title, content=None):
self.title = title
self.content = content
def __unicode__(self):
return self.title
__str__ = __unicode__

Related

Calling properties using "Self" keyword and One-To-Many Relationships in Django

I'm new to Django, and having some trouble understanding how the self keyword works, compared to this in JS or C#. I've been working through the Django REST API tutorial, and now am trying to add a second Model class. I'd like to have a one-to-many relationship between each Snippet and SubSnippets, but would like to have my SubSnippets inherit the language and style properties of the parent Snippet so that I can use them in the formatter for each SubSnippet. Here's the code I've added to models.py:
class SubSnip(models.Model):
snippet = models.ForeignKey(Snippet, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User', related_name='sub-snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
class Meta:
ordering = ['snippet', 'created']
def save(self, *args, **kwargs):
lexer = get_lexer_by_name(self.snippet.language)
linenos = 'table' if self.linenos else False
options = {'title': self.title} if self.title else {'snippet title': self.snippet.title}
formatter = HtmlFormatter(style=self.snippet.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
The problem is that self.snippet.language doesn't seem to be calling the actual Snippet class, because I'm getting an error that says "Instance of ForeignKey has no 'language' member." Same with self.snippet.title and self.snippet.style.
I find the convention of putting all Models in a single file a little strange, and I'm wondering if, from the compiler POV, that is why I can't access the properties of the Snippet class. Or is it something else about how Models work in Django/Python? I'd love to get a deeper understanding of what's happening here!
There are a few questions here so I'll address one at a time.
As for your model structure, you can do something like this:
Keep in mind - in python, when we pass an object to a class declaration, that class will inherit the objects properties, attributes, and methods.
# Snippet inherits the default django model:
class Snippet(models.Model):
# fields:
title = models.ChardField(...)
...
# this simple method returns the title:
def get_title(self):
return self.title
# SubSnippet inherits the Snippet class:
class SubSnippet(Snippet):
# fields:
snippet = models.ForeignKey('Snippet', ...)
...
Now we can instantiate some instances like so:
# create a snippet:
snippet = Snippet.objects.create(
title='I am the parent'
)
# create a subsnippet, and assign the above snippet as its 'parent':
subsnippet = SubSnippet.objects.create(
title='I am the child',
snippet=snippet
)
# accessing fields:
print(subsnippet.title) # prints 'I am the child'
print(subsnippet.snippet.title) # prints 'I am the parent'
print(snippet.title) # prints 'I am the parent'
# using methods:
print(snippet.get_title()) # prints 'I am the parent'
print(subsnippet.get_title()) # prints 'I am the child'

Declaring PeeWee models inside class, passing database parameter to BaseModel

I'm not sure if it's reasonable what I'm tring to do, but I want to put all classes and methods connected with database management in one single class.
So there will be DataSaver class representing one instance of database.
Now, official PeeWee docs recommend to create BaseModel and store database variable there. Here is how it's shown in example:
from peewee import *
db = SqliteDatabase('my_app.db')
class BaseModel(Model):
class Meta:
database = db
class User(BaseModel):
username = CharField(unique=True)
class Tweet(BaseModel):
user = ForeignKeyField(User, backref='tweets')
message = TextField()
created_date = DateTimeField(default=datetime.datetime.now)
is_published = BooleanField(default=True)
Now I'm trying to do the same thing, but inside class:
class DataSaver:
def __init__(self, database_save_path):
self.database_save_path = database_save_path
self.db = SqliteDatabase(database_save_path)
db.connect()
db.create_tables([User, Chat, Message], True)
class BaseModel(Model):
class Meta:
database = self.db
class User(BaseModel):
name = CharField(unique=True)
class Chat(BaseModel):
name = CharField(unique=True)
And the problem here is: BaseModel don't have currently access to variable db. self currently isn't pointing to DataSaver so it's rather clear that complier can be a little confused at this point.
Do you have any idea how to pass db variable to BaseModel so second block of code will work similarily as the first one?
I don't think you're correct. self is still the DataSaver instance at the time when you are declaring database = self.db. Is there an actual error you're getting?
I know, this question is old, but since I researched it for myself, here is my solution.
You should use one of the techniques described in the peewee documentation
class DataSaverA(object):
def __init__(self, database_save_path):
self.database_save_path = database_save_path
self.db = SqliteDatabase(database_save_path)
self.db.bind([DataSaverA.User, DataSaverA.Chat])
self.db.connect()
self.db.create_tables([DataSaverA.User, DataSaverA.Chat])
class BaseModel(Model):
pass
class User(BaseModel):
name = CharField(unique=True)
class Chat(BaseModel):
name = CharField(unique=True)
This way, you can for instance have a class DataSaverB, which needs to declare its own User and Chat inner classes, instantiate users like
u1 = DataSaverA.User.create(name='Uncle Bob')
u2 = DataSaverB.User.create(name='Grandma L.')
However, I think the use of inner classes is bad practice here, snce you would have to replicate the definitions of the inner classes for all DataSaver variants.
In my case, I was more interested in grouping functionality and configuration, instead of actually relying on multiple datasavers, so I dropped the idea of using nested classes.

Slug field in SQL Alchemy/FlaskAdmin

I am trying to create a slug field in one of my DB models. I am using Flask-SQLAlchemy and Flask-Admin. My Class is defined as:
class Merchant(db.Model):
__tablename__ = 'merchants'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String())
...
slug = db.Column(db.String())
def __repr__(self):
return 'Merchant: {}'.format(self.name)
I have attempted to implement something like this from this question:
def __init__(self, *args, **kwargs):
if not 'slug' in kwargs:
kwargs['slug'] = slugify(kwargs.get('name', ''))
super().__init__(*args, **kwargs)
However it does not work when I am creating the new Merchant using Flask-Admin.
In trying to determine why, I change the init function to simply print kwargs. When I create a new Merchant in Flask-Admin, it prints {}, however when I do it in the python shell, like Merchant(name ='test'), it prints {'name' : 'test'}.
Does anyone know how I can access the arguments being passed to my class at init from Flask-Admin?
It looks like Flask-Admin is not passing constructor arguments when initialising the new object and your code doesn't have any effect. I haven't checked the source code, but they could be doing something like:
m = Merchant()
...
m.name = 'foo'
I think your best bet if you want to continue using Flask-Admin would be extending the ModelView and implementing the on_model_change hook:
class MerchantModelView(BaseModelView):
def on_model_change(self, form, model, is_created):
if is_created and not model.slug:
model.slug = slugify(model.name)
You can fine more information in the documentation.

Change model representation in Flask-Admin without modifying model

I have a model with a __repr__ method, which is used for display in Flask-Admin. I want to display a different value, but don't want to change the model. I found this answer, but that still requires modifying the model. How can I specify a separate representation for Flask-Admin?
class MyModel(db.Model):
data = db.Column(db.Integer)
def __repr__(self):
return '<MyModel: data=%s>' % self.data
Update
File: models.py
class Parent(db.Model):
__tablename__ = "parent"
id = db.Column(db.Integer, primary_key=True)
p_name = db.Column(db.Text)
children = db.relationship('Child', backref='child', lazy='dynamic')
def __repr__(self):
return '<Parent: name=%s' % self.p_name
class Child(db.Model):
__tablename__ = "child"
id = db.Column(db.Integer, primary_key=True)
c_name = db.Column(db.Text)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))
File: admin.py
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from app import app, db
from models import Parent, Child
admin = Admin(app, 'My App')
admin.add_view(ModelView(Parent, db.session))
admin.add_view(ModelView(Child, db.session))
When I try to create or edit "child" through admin panel, I see representation from "Parent" class. I suppose it is because of relationship and I don't know how to redefine the representation for admin panel only.
The following answers have helped me to solve my issue:
How to tell flask-admin to use alternative representation when displaying Foreign Key Fields?
Flask-admin, editing relationship giving me object representation of Foreign Key object
Flask-Admin Many-to-Many field display
The cause was in that I tried to replace __repr__ with __unicode__ instead just add __unicode__ method.
But if anybody knows solution without modifying models, let me know and I'll add it here.
You could subclass the model:
class MyNewModel(MyModel):
def __repr__(self):
return '<MyModel: DATA IS %d!>' % self.data
and then use MyNewModel instead of MyModel.
I have the same problem and I've found this solve:
class Child(Parent):
def __repr__(self):
return '<Child: name=%s' % self.p_name
setattr(Parent, '__repr__', Child.__repr__)
It overloads Parent.__repr__, but now you can not to change SQLA model.

How do I extend a SQLAlchemy bound declarative model with extra methods?

For example, I have a declarative class on module a:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
Now, in module b I want to use the mapped entity, but add a method:
from a import User
class UserWithExtraMethod(User):
def name_capitalized(self):
return self.name.capitalize()
user = UserWithExtraMethod()
print(user.name_capitalized)
However, when I run the script, I will get the following error:
InvalidRequestError: Multiple classes found for path "User" in the registry of this declarative base. Please use a fully module-qualified path.
What have I missed when declaring the user entity? I would like to reuse the previous declared entity.
I am expecting something would be like:
class UserWithExtraMethod(User):
___magic_reuse_previous_mapper__ = True
def name_capitalized(self):
return self.name.capitalize()
Unless you've got a particular reason to have separate classes, you should just write:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
def name_capitalized(self):
return self.name.capitalize()
Since the name_capitalized is not special as far as SQLAlchemy is concerned (it's not a ColumnExpression or some such), it is completely ignored by the mapper.
Actually, there's an even better way to do this; your version works fine for instances of User, but is of no use in sql expressions.
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
class User(Base):
# ... body as before
#hybrid_method
def name_capitalized(self):
return self.name.capitalize()
#name_capitalized.expression
def name_capitalized(cls):
# works for postgresql, other databases spell this differently.
return sqlalchemy.func.initcap(cls.name)
which will allow you to do things like:
>>> print Query(User).filter(User.name_capitalized() == "Alice")
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE initcap(users.name) = :initcap_1
Perhaps a little late for this reply. Do you have any other relationships setup that are pointing to User?
For example, if you have Address defined as:
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
address = Column(String(50))
Users = relationship("User", backref="addresses")
when Address is trying to resolve to which User within the declarative base to point to, it will find two of them. To verify try Base._decl_class_registry['User']. This is similar to this topic covered by Michael.
In ./sqlalchemy/ext/declarative/clsregistry.py there is an example on how to use the fully qualified path. In this case it would be changing the relationship within address from Users = relationship("User", backref="addresses") to
Users = relationship("a.User", backref="addresses")
Hope this helps point you in the right direction for debugging.
Hacky, but why not just monkey-patch the User class for your purpose instead of inheriting from it?
# modude b
from a import User
def name_capitalized(self):
return self.name.capitalize()
User.name_capitalized = name_capitalized
user = User() # and it has extra-method as well
print(user.name_capitalized)
This may not work for you. I had a similar issue. I ended up passing an instance of User to UserWithExtraMethod during instantiation
class UserWithExtraMethod(object):
def __init__(self, user):
self.user = user
def name_capitalized(self):
return self.user.name.capitalize()
Hope this helps

Categories

Resources