Flask-Admin Cannot find reverse relation - flask

I already have an application using my SQLAlchemy model, and I'm now trying to add a flask admin website on top to manage some of the data in the DB. This model is already setup and working throughout the existing application so I know the relationships are configured correctly.
In my DB the data_package table has a FK to the supplier table, and this has been added as a relationship. However, when I create a flask model view to allow editing of the data_package I'm receiving the error:
Exception: Cannot find reverse relation for model
I've added a subset of the code below, there are a lot more columns and logic but this is the bare minimum I have to try and test a fix to the issue.The issue is happening because I am adding inline_models = (Supplier,) which I need as I want to have a select to allow the supplier to be changed for a data package.
The line of code throwing the exception is https://github.com/flask-admin/flask-admin/blob/master/flask_admin/contrib/sqla/form.py#L558. The reason for this is the relationship direction is defined as ONETOMANY, and there is a check to ensure the direction is MANYTOONE or MANYTOMANY
Base = declarative_base()
class Supplier(Base):
__tablename__ = 'supplier'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
class DataPackage(Base):
__tablename__ = 'data_package'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
supplier_id = Column(Integer, ForeignKey('supplier.id'), index=True, nullable=False)
supplier = relationship("Supplier", backref='packages')
class DataPackageAdminView(ModelViewOnly):
form_columns = ['name', 'supplier']
inline_models = (Supplier,)
def create_app():
settings.configure_orm()
app = Flask(__name__)
app.config.from_pyfile('config.py')
#app.route('/')
def index():
return 'Click me to get to Admin!'
admin = Admin(app, name='Test Editor', template_mode='bootstrap3')
admin.add_view(DataPackageAdminView(DataPackage, settings.Session, category='Data Sources'))
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)

Related

How to only create relevant model fields during a django test?

I am testing a method that requires me to create a fake record in my model. The model has over 40 fields. Is it possible to create a record with only the relevant model fields for the test so I don't have to populate the other fields? If so how would I apply it to this test case example.
models.py
class Contract():
company = models.CharField(max_length=255),
commission_rate = models.DecimalField(max_digits=100, decimal_places=2)
type = models.CharField(max_length=255)
offer = models.ForeignKey('Offer', on_delete=models.PROTECT)
notary = models.ForeignKey('Notary', on_delete=models.PROTECT)
jurisdiction = models.ForeignKey('Jurisdiction', on_delete=models.PROTECT)
active = models.BooleanField()
...
test.py
import pytest
from app.models import Contract
def calculate_commission(company, value):
contract = Contract.objects.get(company='Apple')
return value * contract.commission_rate
#pytest.mark.django_db
def test_calculate_commission():
#The only two model fields I need for the test
Contract.objects.create(company='Apple', commission_rate=0.2)
assert calculate_commission('Apple', 100) == 20
Try to use model_bakery to make an object record. Just populate fields you want and leave another blank, model_bakery will handle it. For the Detail, you can check this out model_bakery

Can't add value from other DB on many-to-may field in django

Here's my code
a = Approver.objects.create(title = 'Tester')
a.save()
p = Profile.objects.create(name="Test")
p.save()
a.users.add(p)
I'm getting this error after
ValueError: Cannot add "<Profile: TEST>": instance is on database "default", value is on database "profiles_db"
Here's my model
class Approver(models.Model):
title = models.CharField(max_length=255)
users = models.ManyToManyField(Profile, blank=True)
The Profile model come from another app which connects to a different database.
Is it not possible?

Flask Admin One to One Relationship and Edit Form

I am building an admin dashboard for my web app using Flask-Admin. For the user/address relationship, I am using a one to one relationship. On the user edit form, I'd like to be able to edit the individual components of the address (i.e. street address, city or zip) similar to what inline_models provides. Instead, flask-admin generates a select field and only allows me to select a different addresses.
I tried using inline_models = ['address'] in the UserModelView definition. However, I got the address object not iterable error due to the user/address relationship being configured to uselist=False. Switching uselist to True would affect other parts of my code, so I'd prefer to leave it as False.
From looking in flask-admin/contrib/sqla/forms, within the function get_forms, its being assigned a one to many tag which is what drives the use of a select field.
Before diving in further, I figured it best to see if anyone else has come across this or has a recommended fix/workaround.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64))
address = db.relationship("Address", backref="user",
cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
line1 = db.Column(db.String(128))
zip = db.Column(db.String(20), index=True)
city = db.Column(db.String(64), index=True, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id",
ondelete="CASCADE"))
admin.py
class UserModelView(ModelView):
column_list = [User.username, 'address']
form_columns = (User.username, 'address')
admin = Admin(name='Ask', template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))
You can create 2 relations
# Relation for flask admin inline model
address_cms_relationsip = db.relationship(
"Address", backref="user", cascade="all, delete-orphan", lazy=False,
uselist=True, passive_deletes=True)
address_relationship = db.relationship(
"Address", cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
#property
def address(self):
return self.address_relationship
In your code you can use property address
user: User # some User object
user.address.city

foreignkey backwards manager not working django

This seems simple.. I have a Restaurant model and Recipe model, all I want is to be able to look up all the Recipes by a certain Restaurant. I am using the backwards manager FOO_set and cant seem to make it work:
models.py
class Restaurant(models.Model):
restaurant_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='restaurant')
class Recipe(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, related_name='recipe')
views.py
def dashboard(request):
user = request.user
try:
user.restaurant
recipe_list = user.restaurant.recipe_set
return render(request, 'usermanage/dashboard_restaurant.html',{'recipe_list':recipe_list})
When i run this i get an exception in the dashboard view. Looking at my database the user.restaurant object has recipes associated with it. If i just do recipe_list = user.restaurant it returns the restaurant object. Just when I add recipe_set it fails. Also tried recipe_set.all and all(). I have migrated everything. Django 1.11
you set related_name so you need to use it:
replace:
recipe_list = user.restaurant.recipe_set
# ^^^^^
to
recipe_list = user.restaurant.recipe.all()
# ^^^^^^

manage QuerySelectField across Flask blueprints

I have been through various tutorials + search on Stackoverflow / Google, but failed to find some answers on how to use QuerySelectField using several Flask blueprints, may be you could help.
Basically, I have defined a Project class and a Client class in my models.py as follows:
class Project(db.Model):
__tablename__ = 'projects'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128), nullable=False)
description = db.Column(db.Text)
client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
class Client(db.Model):
__tablename__ = 'clients'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
location = db.Column(db.Text, nullable=False)
projects = db.relationship('Project', lazy='dynamic', backref='project')
I then have a blueprint for each part, one to manage my projects, another one for my clients.
I then get stuck when I try to use a QuerySelectField to retrieve the client names in my projects form, here is what I have so far in my projects/forms.py:
from flask.ext.wtf import Form
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from wtforms.validators import Length, Required
from wtforms.fields.html5 import DateField
class ProjectForm(Form):
title = StringField('Title', validators=[Required(), Length(1, 128)])
description = TextAreaField('Desciption')
date = DateField('Date', format='%d/%m/%Y')
client_id = QuerySelectField('Select Client', validators=[Required()], coerce=int, choices = [(1, 'abc'), (2, 'defg'), (3, 'hij')])
submit = SubmitField('Submit')
def from_model(self, project):
self.title.data = project.title
self.description.data = project.description
self.client_id.data = project.client_id
self.date.data = project.date
def to_model(self, project):
project.title = self.title.data
project.description = self.description.data
project.client_id = self.client_id.data
project.date = self.date.data
I know this version is NOT picking any client as I am passing static data i/o dynamic ones, but at least it works and I can see my SelectField with possible values. I have read I should be using something like
client_id = QuerySelectField('Select Client', query_factory=lambda: Client.query.all())
and been also recommended to first declare something like the following before "class ProjectForm(Form)":
def client_id():
return Client.query.filter_by(enabled=True)
In all cases, I end up with a NameError: name 'Client' is not defined, I understand I have to say somewhere that the program should look at my class Client, but I failed finding where, unless the issue is somewhere else.
Thanks in advance for your help.
I did it simply this way:
def get_clients():
from forms import Client
return Client.query.all()
and then:
client_id = QuerySelectField('Select Client', query_factory=get_clients)