Cross-database join in Flask-SQLAlchemy - join

I'm trying to do a cross-database join in Flask-SQLAlchemy:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = '...Master...'
app.config['SQLALCHEMY_BINDS'] = { 'Billing': '...Billing...' }
db = SQLAlchemy(app)
class Account(db.Model):
__tablename__ = 'Accounts'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255))
class Setting(db.Model):
__tablename__ = 'Settings'
AccountId = db.Column(db.Integer, db.ForeignKey(Account.id), primary_key=True)
Enabled = db.Column(db.Boolean)
class BillingAccount(db.Model):
__tablename__ = 'Account'
__bind_key__ = 'Billing'
id = db.Column(db.Integer, primary_key=True)
AccountId = db.Column(db.Integer, db.ForeignKey(Account.id))
currency = db.Column(db.Integer)
class AccountSetting(db.Model):
__table__ = db.join(Account, AccountSetting)
id = db.column_property(Account.id, AccountSetting.AccountId)
username = Account.username
enabled = Setting.Enabled
class AccountSettingBilling(db.Model):
__table__ = db.join(Account, AccountSetting).join(BillingAccount)
id = db.column_property(Account.id, AccountSetting.AccountId, BillingAccount.AccountId)
username = Account.username
enabled = Setting.Enabled
currency = BillingAccount.currency
With this I can successfully query AccountSetting.query.all() but not AccountSettingBilling.query.all(), which fails with error 208 (MSSQL for 'object does not exist').
If I examine the generated SQL I can clearly see that it is doing a JOIN on Account.AccountId=Accounts.id when I'd expect to see some reference to Billing, e.g. Billing.Account.AccountId=Accounts.id.
Having followed Cross database join in sqlalchemy and http://pythonhosted.org/Flask-SQLAlchemy/binds.html it looks to me as though I've done things correctly. What gives?

You define an object db = SQLAlchemy(app) - it is Database1. You refer to it everywhere, but there is no reference to Database2. Also note code refers to columns for the join using 2 part identifiers:
Account . AccountId and Accounts . id
whereas you wish to have 3 part identifiers:
Billing . Account . AccountId and [Accounts] . Accounts . id
You are missing this property for db name from definition of each Class:
__table_args__ = {'schema': 'Accounts'}
__table_args__ = {'schema': 'Billing'}

Related

How to Many2Many field relationship and map that in Invoice in odoo

how we suppose to do that, is it possible to use Many2many fields or we can create another model with vehicle_id and partner_id.
how create and edit vehicle_number fetch automatically cutomer_id many2many field when vehicle already existing vehicle number with different partner_id
class VehicleMaster(models.Model):
_name = "vehicle.master.model"
_description = "Vehicle Master"
_rec_name = 'x_vehicle_number_id'
x_brand_id = fields.Many2one('vehicle.brand.model', string="Brand Name",ondelete='restrict')
x_model_id = fields.Many2one('vehicle.model.model', string="Model Name",domain=" [('brand_id','=',x_brand_id)]",ondelete='restrict')
x_vehicle_number_id = fields.Char(string="Vehicle Number", required=True, tracking=True,ondelete='restrict')
x_customer_id = fields.Many2many('res.partner',string="Customers",required=True,tracking=True)
class CustomInvoice(models.Model):
_inherit = "account.move"
vehicle_number = fields.Many2one('vehicle.master.model',string='Vehicle number',domain="[('x_customer_id','=',partner_id)]")
I try to used vehicle.master.model in field many2many vehicle_number and relation with invoice module when create vehicle_number in invoice to not fetch automatically when vehicle_number is already created with different partner_id.

How to amend Models.py to Changes url representation

I am implementing a project on django rest framework (filemanager). I need the url field for the "FILE" data type to be in this form as "url": "/file/url1". For the "FOLDER" class, the "url" field should output "url": None. What do I need to write for this in model.py .
Current model.py code looks like:
FILE='FILE'
FOLDER='FILE'
TYPE_CHOICES = [(FILE,"FILE"),(FILE,"FOLDER")]
class Folder(models.Model):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, null=False)
parentId = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=255, choices=TYPE_CHOICES, editable=False, default='FOLDER')
size = models.IntegerField(blank=True, null=True, default = 0)
date = models.DateTimeField(auto_now=True)
url = None
# children = models.ForeignKey('self', models.SET_NULL, null=True)
class File(models.Model):
type = models.CharField(max_length=255, choices=TYPE_CHOICES, editable=False, default='FILE')
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, null=False)
file = models.FileField(null=True, max_length=255)
date = models.DateTimeField(auto_now =True)
parentId = models.ForeignKey(Folder, on_delete=models.CASCADE, null=True, blank=True, related_name='children')
# url = models.URLField(max_length=200, default=None, null=True, blank=True)
size = models.IntegerField(null=True, blank=True, default=0)
def __str__(self):
return str(self.file.name)

Django Admin Change Form : How To avoid using __str__ in header of Django Admin Change Form?

Hi does anyone know if it is possible to avoid Django rendering str for model when in the change admin form? My str method has multiple attributes rendered as shown below:
class Product(models.Model):
"""
Product
"""
id = models.PositiveBigIntegerField(primary_key=True)
attributes = models.JSONField()
labels = models.JSONField()
name = models.TextField()
relative_url = models.TextField(max_length=250)
image = models.URLField()
delivery = models.TextField(max_length=50)
online = models.BooleanField()
is_customizable = models.BooleanField()
is_exclusive = models.BooleanField()
url = models.URLField()
max_price = models.PositiveIntegerField()
min_price = models.PositiveIntegerField()
currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
discount_percentage = models.PositiveSmallIntegerField(default=0)
recommended_retail_price = models.PositiveIntegerField()
def __str__(self) -> str:
product_str = (
f"id: {self.id}\n"
f"attributes: {self.attributes}\n"
f"labels: {self.labels}\n"
f"name: {self.name}\n"
f"relative_url: {self.relative_url}\n"
f"image: {self.image}\n"
f"delivery: {self.delivery}\n"
f"online: {self.online}\n"
f"is_customizable: {self.is_customizable}\n"
f"is_exclusive: {self.is_exclusive}\n"
f"url: {self.url}\n"
f"max_price: {self.max_price}\n"
f"min_price: {self.min_price}\n"
f"currency: {self.currency}\n"
f"discount_percentage: {self.discount_percentage}\n"
f"recommended_retail_price: {self.recommended_retail_price}\n"
)
return product_str

Undefined join condition between parent/child tables

Here's a model for a basic todo app, in which a user can create a tasklist that contains tasks:
from flask.ext.sqlalchemy import SQLAlchemy
from allot import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
tasklists = db.relationship('Tasklist', backref='user', lazy='dynamic')
tasks = db.relationship('Task', backref='user', lazy='dynamic')
def __init__(self, username, email):
self.username = username
class Tasklist(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('User.id'))
tasks = db.relationship('Task', backref='tasklist', lazy='dynamic')
def __init__(self, title, description, user_id):
self.title = title
self.user_id = user_id
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title= db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('User.id'))
tasklist_id = db.Column(db.Integer, db.ForeignKey('Tasklist.id'))
def __init__ (self, title, status, user_id):
self.title = title
self.user_id = user_id
self.tasklist_id = tasklist_id
It generates this error:
ArgumentError: Could not determine join condition between parent/child
tables on relationship User.tasklists. Specify a 'primaryjoin'
expression. If 'secondary' is present, 'secondaryjoin' is needed as
well.
Adding primaryjoin arguments in the relationships, like this:
tasklists = db.relationship('Tasklist', backref='user', primaryjoin='user.id==Tasklist.user_id', lazy='dynamic')
... returns:
InvalidRequestError: One or more mappers failed to initialize - can't
proceed with initialization of other mappers.
What's wrong with the model?
I searched for an explanation in the [relationship][1] doc and on SO but to no avail.
One possibility is that when you're referencing foreign keys your using the uppercase "User" when you actually need to use the lowercase tablename reference. Try this:
from flask.ext.sqlalchemy import SQLAlchemy
from allot import db
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
tasklists = db.relationship('Tasklist', backref='user', lazy='dynamic')
tasks = db.relationship('Task', backref='user', lazy='joined'))
def __init__(self, username, email):
self.username = username
class Tasklist(db.Model):
__tablename__ = 'tasklist'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
tasks = db.relationship('Task', backref='tasklist', lazy='dynamic')
def __init__(self, title, description, user_id):
self.title = title
self.user_id = user_id
class Task(db.Model):
__tablename__ = 'task'
id = db.Column(db.Integer, primary_key=True)
title= db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
tasklist_id = db.Column(db.Integer, db.ForeignKey('tasklist.id'))
def __init__ (self, title, status, user_id):
self.title = title
self.user_id = user_id
self.tasklist_id = tasklist_id

Order Django admin change list column by output of __unicode()__ method

Here's part of my Django app's models.py:
class Person(models.Model):
birth_year = WideYear(null=True, blank=True)
birth_year_uncertain = models.BooleanField()
death_year = WideYear(null=True, blank=True)
death_year_uncertain = models.BooleanField()
flourit_year = WideYear(null=True, blank=True)
flourit_year_uncertain = models.BooleanField()
FLOURIT_CHOICES = (
(u'D', u'Birth and death dates'),
(u'F', u'Flourit date'),
)
use_flourit = models.CharField('Date(s) to use', max_length=2, choices=FLOURIT_CHOICES)
index_entries = models.ManyToManyField(IndexEntry, null=True, blank=True)
def __unicode__(self):
if self.personname_set.filter(default_name__exact=True):
name = z(self.personname_set.filter(default_name__exact=True)[0])
else:
name = u'[Unnamed person]'
if self.use_flourit == u'D':
dates = '%s - %s' % (z(self.birth_year), z(self.death_year))
else:
dates = 'fl. ' + z(self.flourit_year)
return '%s (%s)' % (name, dates)
class PersonName(models.Model):
titles = models.CharField(max_length=65535, null=True, blank=True)
surname = models.CharField(max_length=255, null=True, blank=True)
first_name = models.CharField(max_length=255, null=True, blank=True)
middle_names = models.CharField(max_length=255, null=True, blank=True)
post_nominals = models.CharField(max_length=65535, null=True, blank=True)
default_name = models.BooleanField()
person = models.ForeignKey(Person, null=True, blank=True)
def __unicode__(self):
return '%s, %s %s' % (self.surname, self.first_name, self.middle_names)
class Meta:
unique_together = ("titles", "surname", "first_name", "middle_names", "post_nominals", "person")
unique_together = ("default_name", "person")
and here are the corresponding parts of my app's admin.py:
from reversion.admin import VersionAdmin
class PersonNameInline(admin.TabularInline):
model = PersonName
extra = 1
class PersonAdmin(VersionAdmin):
radio_fields = {"use_flourit": admin.HORIZONTAL}
inlines = [PersonNameInline]
admin.site.register(Person, PersonAdmin)
In the admin, this produces a change list as follows:
(source: sampablokuper.com)
As you can see, although the change list populates each row of the Person column with the output of the __unicode()__ method of the Person class, it does not order the rows of that column by the __unicode()__ method of the Person class.
How can I make it do so?
Many thanks in advance!
Django ordering is done in the database level. Unless you store the result of your unicode function in the DB, django is not going to be able to natively return results ordered in that fashion.
Storing an ordering value in the DB is probably the most expedient way to solve this problem.
Just hit the same problem, and I found the following link useful. Instead of sorting by unicode, it tries to sort by multiple columns, which may help solve the problem:
http://djangosnippets.org/snippets/2110/
Following Paul McMillan's suggestion, I added the following line to the class definition of Person:
ordering_string = models.CharField(max_length=255, null=True, blank=True)
I also put the this above the PersonName definition in models.py:
def post_save_person_and_person_name(sender, **kwargs):
person_name = kwargs['instance']
person = person_name.person
if person.ordering_string != unicode(person)[0:254]:
person.ordering_string = unicode(person)[0:254]
super(Person, person).save()
and put this below the PersonName definition in models.py:
post_save.connect(post_save_person_and_person_name, sender=PersonName)
So far, so good.
I think I might be able to improve on it by replacing the save() call above with a queryset update() . I'd welcome suggestions on that front!

Resources