Change model representation in Flask-Admin without modifying model - python

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.

Related

How to set hash of a column in another column in flask-admin?

I want to save hash of name to hash_name column Also I use Flask-Admin to manage my data.
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Unicode, unique=True, nullable=False)
hash_name = db.Column(db.Unicode, unique=True)
admin.add_view(ModelView(User, db.session))
Also I set default with uuid package for hash_name but this page in result had a problem .my uuid never changed . I refreshed but not changed
If you only use flask-admin's SQLAlchemy ModelViews for editing, then it's possible to do following:
class UserView(sqla.ModelView):
# Hide `hash_name` in list and form views
column_exclude_list = ('hash_name',)
form_excluded_columns = ('hash_name',)
# Generate new hash on `name` change
def on_model_change(self, form, model, is_created):
if len(model.name):
model.hash_name = generate_hash_name(model.name)
Otherwise use #mehdy's event approach.
I think you can use sqlalchemy's even listeners to manipulate your object before committing it to the database:
from sqlalchemy import event
...
#event.listens_for(User, "before_commit")
def gen_default(mapper, connection, instance):
instance.hash_name = hash_function(instance.name)
so before each commit it will be invoked and updates the hash_name attribute with the proper hash on name

Sqlalchemy - Foreign Value

I'm looking for a way to do the following.
class Foo(db.Model):
__tablename__ = 'foos_foo'
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
# Not an entity in the table but
# whenever foo.author_name is called,
# it selects the value from User table
author_name = author.name
The reason why I'm looking for a way to do is this:
class Foo(db.Model, Serializable):
I made a Serializable mixin so that foo.serialize would simply return row values in json.
I wish author.name to be part of this serialization. Of course, there are countless other ways to get author's name and insert it inside the serialized output, but for the benefit of clean code, I wish to find a way to include foreign value in the model.
I use the misnomer 'foreign key' because I have no idea what the most appropriate keyword is.
Thank you in advance.
I ended up with using Marshmallow for object serialization.
http://marshmallow.readthedocs.org/en/latest/index.html
These are the implementations.
model.py
class User(db.Model):
name = db.Column()
class UserSerializer(Serializer):
class Meta:
fields = ('id', 'name')
class FooSerializer(Serializer):
author_name = fields.Nested('UserSerializer')
class Meta:
fields = ('id', 'author_name')
view.py
foos = Foo.query.all()
dict = FooSerializer(foos, many=True).data
Objects are not serialize able.
I think you need implement a method that convert to JSON.
json.dumps works well with dicts. So you can also look at.
http://www.marcstober.com/blog/2007/07/07/serializing-arbitrary-python-objects-to-json-using-dict/
You can implement your own method to_json also.

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

django abstract models versus regular inheritance

Besides the syntax, what's the difference between using a django abstract model and using plain Python inheritance with django models? Pros and cons?
UPDATE: I think my question was misunderstood and I received responses for the difference between an abstract model and a class that inherits from django.db.models.Model. I actually want to know the difference between a model class that inherits from a django abstract class (Meta: abstract = True) and a plain Python class that inherits from say, 'object' (and not models.Model).
Here is an example:
class User(object):
first_name = models.CharField(..
def get_username(self):
return self.username
class User(models.Model):
first_name = models.CharField(...
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(...
I actually want to know the difference between a model class that
inherits from a django abstract class (Meta: abstract = True) and a
plain Python class that inherits from say, 'object' (and not
models.Model).
Django will only generate tables for subclasses of models.Model, so the former...
class User(models.Model):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(max_length=255)
...will cause a single table to be generated, along the lines of...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
...whereas the latter...
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User):
title = models.CharField(max_length=255)
...won't cause any tables to be generated.
You could use multiple inheritance to do something like this...
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User, models.Model):
title = models.CharField(max_length=255)
...which would create a table, but it will ignore the fields defined in the User class, so you'll end up with a table like this...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
An abstract model creates a table with the entire set of columns for each subchild, whereas using "plain" Python inheritance creates a set of linked tables (aka "multi-table inheritance"). Consider the case in which you have two models:
class Vehicle(models.Model):
num_wheels = models.PositiveIntegerField()
class Car(Vehicle):
make = models.CharField(…)
year = models.PositiveIntegerField()
If Vehicle is an abstract model, you'll have a single table:
app_car:
| id | num_wheels | make | year
However, if you use plain Python inheritance, you'll have two tables:
app_vehicle:
| id | num_wheels
app_car:
| id | vehicle_id | make | model
Where vehicle_id is a link to a row in app_vehicle that would also have the number of wheels for the car.
Now, Django will put this together nicely in object form so you can access num_wheels as an attribute on Car, but the underlying representation in the database will be different.
Update
To address your updated question, the difference between inheriting from a Django abstract class and inheriting from Python's object is that the former is treated as a database object (so tables for it are synced to the database) and it has the behavior of a Model. Inheriting from a plain Python object gives the class (and its subclasses) none of those qualities.
The main difference is how the databases tables for the models are created.
If you use inheritance without abstract = True Django will create a separate table for both the parent and the child model which hold the fields defined in each model.
If you use abstract = True for the base class Django will only create a table for the classes that inherit from the base class - no matter if the fields are defined in the base class or the inheriting class.
Pros and cons depend on the architecture of your application.
Given the following example models:
class Publishable(models.Model):
title = models.CharField(...)
date = models.DateField(....)
class Meta:
# abstract = True
class BlogEntry(Publishable):
text = models.TextField()
class Image(Publishable):
image = models.ImageField(...)
If the Publishable class is not abstract Django will create a table for publishables with the columns title and date and separate tables for BlogEntry and Image. The advantage of this solution would be that you are able to query across all publishables for fields defined in the base model, no matter if they are blog entries or images. But therefore Django will have to do joins if you e.g. do queries for images...
If making Publishable abstract = True Django will not create a table for Publishable, but only for blog entries and images, containing all fields (also the inherited ones). This would be handy because no joins would be needed to an operation such as get.
Also see Django's documentation on model inheritance.
Just wanted to add something which I haven't seen in other answers.
Unlike with python classes, field name hiding is not permited with model inheritance.
For example, I have experimented issues with an use case as follows:
I had a model inheriting from django's auth PermissionMixin:
class PermissionsMixin(models.Model):
"""
A mixin class that adds the fields and methods necessary to support
Django's Group and Permission model using the ModelBackend.
"""
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
class Meta:
abstract = True
# ...
Then I had my mixin which among other things I wanted it to override the related_name of the groups field. So it was more or less like this:
class WithManagedGroupMixin(object):
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
related_name="%(app_label)s_%(class)s",
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
I was using this 2 mixins as follows:
class Member(PermissionMixin, WithManagedGroupMixin):
pass
So yeah, I expected this to work but it didn't.
But the issue was more serious because the error I was getting wasn't pointing to the models at all, I had no idea of what was going wrong.
While trying to solve this I randomly decided to change my mixin and convert it to an abstract model mixin. The error changed to this:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
As you can see, this error does explain what is going on.
This was a huge difference, in my opinion :)
The main difference is when you inherit the User class. One version will behave like a simple class, and the other will behave like a Django modeel.
If you inherit the base "object" version, your Employee class will just be a standard class, and first_name won't become part of a database table. You can't create a form or use any other Django features with it.
If you inherit the models.Model version, your Employee class will have all the methods of a Django Model, and it will inherit the first_name field as a database field that can be used in a form.
According to the documentation, an Abstract Model "provides a way to factor out common information at the Python level, whilst still only creating one database table per child model at the database level."
I will prefer the abstract class in most of the cases because it does not create a separate table and the ORM does not need to create joins in the database. And using abstract class is pretty simple in Django
class Vehicle(models.Model):
title = models.CharField(...)
Name = models.CharField(....)
class Meta:
abstract = True
class Car(Vehicle):
color = models.CharField()
class Bike(Vehicle):
feul_average = models.IntegerField(...)

Having some trouble creating my Model in Pylons

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__

Categories

Resources