how to join two model of two separated database python peewee - python

I'm using peewee to use Mysql database.
import peewee
db_1 = peewee.MySQLDatabase('db_1', **{'user': 'root'})
db_2 = peewee.MySQLDatabase('db_2', **{'user': 'root'})
class User(peewee.Model):
id = peewee.PrimaryKeyField()
username = peewee.CharField()
class Meta:
database = db_1
class Tweet(peewee.Model):
id = peewee.PrimaryKeyField()
tweet = peewee.CharField()
user = peewee.IntegerField(db_column='user_id')
class Meta:
database = db_2
I want to execute this code:
m = Tweet().select(Tweet, User).join(
User, join_type=JOIN_INNER, on=User.id == Tweet.user
).where(
User.id == 13
)
but it raises this error:
peewee.ProgrammingError: (1146, "Table 'db_2.user' doesn't exist")
How I can fix it?

I thinkbest solution for problem use view on second database.
I create a views of users into db_2 and that code is work.

Related

Null ID in Django Admin

I'm trying to add a new product in my Products model in Django Admin, yet for some reasons the id field always become null after adding.
Below is my code:
models.py
from django.db import models
class Products(models.Model):
id = models.BigAutoField(primary_key=True) # incremental id
title = models.CharField(max_length=200)
def __str__(self):
return self.title
admin.py
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import *
class ProductsAdminConfig(ImportExportModelAdmin):
model = Products
search_fields = ('title',)
list_filter = ('title', )
list_display = ('id', 'title', )
admin.site.register(Products, ProductsAdminConfig)
Originally I created my database table Products with SQLAlchemy, converting CSV to Mysql. However, I wanted to be able to add a new product inside Django Admin as well.
I have tried emptying my SQL table, clearing my migration folder and run py manage.py makemigrations and py manage.py migrate, but the error still persisted.
When I tried adding a new product, the data rows with null ids still got generated.
Could you suggest me a way to solve this? Thank you!
UPDATE
This is how I create my table
import pandas as pd
from sqlalchemy import create_engine
class DfToMySQL:
def __init__(self, db, host, user, passwd, port, charset='utf8'):
self.db = db
self.host = host
self.user = user
self.passwd = passwd
self.port = port
self.charset = charset
def set_dataframe(self, path):
self.path = path
self.df = pd.read_csv(self.path, encoding="utf-8-sig")
def connect(self):
self.engine = create_engine(f'mysql+pymysql://{self.user}:{self.passwd}#{self.host}:{self.port}/{self.db}')
def drop_duplicated_rows(self, cols):
if cols:
self.df = self.df.drop_duplicates(subset=cols) # empty param if drop all duplicates
else:
print('\nPlease specify column(s) with duplicated rows!')
def to_mysql(self, table_name='table_name'):
self.table_name = table_name
self.df.to_sql(name=self.table_name, con=self.engine.connect(), if_exists = 'replace', index=False)
def print_sample_df(self, row=20):
print(self.df.head(row))
def df_from_mysql(self, table_name):
con = self.engine
df = pd.read_sql(f'SELECT * FROM {table_name}', con=con)
return df
def add_index_column(self):
arr = range(1, len(self.df) + 1)
self.df.insert(0, "index", arr, allow_duplicates=False)
self.df['index'] = self.df['index'].apply(str)
def add_id_column(self):
arr = range(1, len(self.df) + 1)
self.df.insert(0, "id", arr, allow_duplicates=False)
self.df['id'] = self.df['id'].apply(str)
def to_xlsx(self):
self.df.to_excel(r'sample.xlsx', encoding="utf-8-sig", index=False, header=True)
def execute_query(self, query=''):
self.query = query
self.con = self.engine.connect()
self.con.execute(query)
if __name__ == '__main__':
db = 'homing_pigeon2'
user = 'root'
passwd = ''
host = 'localhost'
port = 3306
charset='utf8'
csv_path = r"../csv/products.csv"
table_name = 'products'
d = DfToMySQL(db=db, host=host, user=user, passwd=passwd, port=port, charset=charset)
d.set_dataframe(csv_path)
d.print_sample_df()
d.connect()
d.add_id_column()
d.print_sample_df()
d.to_xlsx()
d.to_mysql(table_name=table_name)
CONCLUSION
I guess when creating a datable using SQLAlchemy with managed=False in the Products model, the only way I could add a new product inside Django Admin without any errors is using ImportExportModelAdmin in admin.py
Your script that creates the table is not including an autoincrement.
I believe you'll need to that after you've run your script. It is the database that assigns the id field for a BigAutoField primary key.
See the mysql documentation at:
https://www.techonthenet.com/mysql/auto_increment.php
I'm not certain of the command to make in existing field to have autoincrement after creation in mysql.
EDIT
See https://stackoverflow.com/a/2169090/4872140
ALTER TABLE document MODIFY COLUMN document_id INT auto_increment
Similar question:
sqlalchemy: how to add a table row with autoincrement in mysql

How to use `migrate()` in Peewee

I am new in peewee and I am very confused about migration. I tried the examples in the official documentation, and I got the results as follows:
from playhouse.migrate import *
from peewee import *
my_db = SqliteDatabase('my_database.db')
migrator = SqliteMigrator(my_db)
from peewee import *
database = SqliteDatabase('my_database.db')
class BaseModel(Model):
class Meta:
database = database
class Product(BaseModel):
name = TextField()
class Meta:
table_name = 'product'
with my_db:
my_db.create_tables([Product,])
Product.create(name = 'Big Peach')
price_field = IntegerField(default=0)
migrate(migrator.add_column('product', 'price', price_field))
db = migrator.database
columns = db.get_columns('product')
print(columns)
Here's what I got and the field of 'price' was added, indicating that migrated () is doing well:
[ColumnMetadata(name='id', data_type='INTEGER', null=False, primary_key=True, table='product', default=None),
ColumnMetadata(name='name', data_type='TEXT', null=False, primary_key=False, table='product', default=None),
ColumnMetadata(name='price', data_type='INTEGER', null=False, primary_key=False, table='product', default=None)]
Now the problem is that when I call Product.price,
query = Product.get(Product.name == 'Big Peach')
print(query.price)
the following result appears:
'Product' object has no attribute 'price'
I've tried another way to do this, in the command line:
python -m pwiz -e sqlite my_database.db
I got this:
from peewee import *
database = SqliteDatabase('my_database.db')
class UnknownField(object):
def __init__(self, *_, **__): pass
class BaseModel(Model):
class Meta:
database = database
class Product(BaseModel):
name = CharField()
price = IntegerField()
class Meta:
table_name = 'product'
I can use these to replace the previous Model, but there's no need to migrate().
So, my confusion is how to call the new field added by using migrate() in ONE .py file.
It's unusual to do this online:
declare a model/schema
migrate it
use the new schema
Because if you know you need a price field just put it on the model class from the start.
If you really need to do this, then you can call this after you've run the migration:
Product._meta.add_field('price', price_field)

bulk_create() of two linked tables on SQLite using peewee : primary key not updated

I have two tables with a 1-n relationship and I would like to insert data in these tables using bulk_create().
Let's take the User and Tweet 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()
I would like to create unsaved instances of User and Tweet and to load them with bulk_create(). A naive solution would be:
db.create_tables([User, Tweet])
john = User(username='John')
mehdi = User(username='Mehdi')
users = [john, mehdi]
tweets = [Tweet(user=john, message='Hello twitter world!'),
Tweet(user=mehdi, message='This is my first message.'),
Tweet(user=mehdi, message='This is my second message.')]
User.bulk_create(users)
Tweet.bulk_create(tweets)
Unfortunately, this does not work because the User instances primary keys are not updated (as stated in the documentation except for Postgres databases).
As it seems impossible to update the primary key of the User instances (even if it were possible, it would probably be very inefficient to read the primary keys from the database), the only solution I can see is to use my own primary key and set it when creating the instance. This would mean not using the very convenient auto-incrementing primary key system of peewee and I would like too know if there is any alternative before to go that way.
This is what I came out with. I would have preferred to avoid messing with peewee's internals but it works fine.
from peewee import *
db = SqliteDatabase('my_app.db')
class BaseModel(Model):
id_counter = 0
id = IntegerField(primary_key=True, constraints=[SQL('AUTOINCREMENT')])
def _set_id(self):
if self.id is None:
self.id = self.__class__.id_counter
self.__class__.id_counter += 1
def save(self, *args, **kwargs):
return super(BaseModel, self).save(*args, **kwargs)
#classmethod
def bulk_create(cls, model_list, batch_size=None):
max_id = cls.select(fn.MAX(cls.id)).scalar() or 0
cls.id_counter = max_id + 1
for model in model_list:
model._set_id()
model._update_fks()
return super(BaseModel, cls).bulk_create(model_list=model_list, batch_size=batch_size)
def _update_fks(self):
for field_name, field in self._meta.fields.items():
if isinstance(field, ForeignKeyField):
fk_field_name = field_name + '_id'
fk_id = getattr(self, field_name).id
setattr(self, fk_field_name, fk_id)
class Meta:
database = db
class User(BaseModel):
username = CharField(unique=True)
class Tweet(BaseModel):
user = ForeignKeyField(User, backref='tweets', field='id')
message = TextField()
db.create_tables([User, Tweet])
# Creating users and tweets one by one
sarah = User.create(username='Sarah')
Tweet.create(user=sarah, message='First tweet in history')
# Bulk user and tweet insertion
john = User(username='John')
mehdi = User(username='Mehdi')
users = [john, mehdi]
tweets = [Tweet(user=john, message='Hello twitter world!'),
Tweet(user=mehdi, message='This is my first message.'),
Tweet(user=mehdi, message='This is my second message.')]
User.bulk_create(users)
Tweet.bulk_create(tweets)
# Creating users and tweets one by one after bulk insertion
miranda = User.create(username='Miranda')
Tweet.create(user=miranda, message='The last tweet')

Join Django model from other database

The situation is as follows in the Django implementation:
Two different database;
Two models with different information
Order number is relation between these models.
I would like to join these models together to get related information. Example
class CurtainOrder(models.Model):
order = models.ForeignKey('curtainconfig.Order', related_name='curtains')
order_number = models.IntegerField(unique=True)
order_type = models.CharField(max_length=50)
class ProductionOrder(models.Model):
id = models.IntegerField(primary_key=True, db_column=u'lOrder_id')
order_number = models.IntegerField(db_column=u'nOrderNumber')
status = models.TextField(db_column=u'cExternalDescription')
class Meta:
app_label = u'backend'
db_table = u'ORDER'
database_name = 'production'
To get the result by an query would be as follow:
SELECT
co.order_number, co.order_type, po.status
FROM
CurtainOrder co
INNER JOIN
[production].dbo.ProductionOrder po on po.order_number = co.order_number
But how can I get the same result in Django code?
I have tried to use SQL query in Django, https://docs.djangoproject.com/en/1.8/topics/db/sql/, like below:
from django.db import connections
cursor = connections['default'].cursor()
sql = """SELECT
co.order_number, co.order_type, po.status
FROM
CurtainOrder co
INNER JOIN
[production].dbo.ProductionOrder po on po.order_number = co.order_number """
cursor.execute(sql)
row = cursor.fetchone()
This is returning the error: return Database.Cursor.execute(self, query)
OperationalError: near ".": syntax error
It's going wrong when the 'production' db is joined, but the SQL query is executed good by doing it directly in the database management app.

SQLite3 OperationalError: Table XYZ has no column named ABC

I am new to peewee, so please forgive me if this is a stupid question. I have searched on Google and in the peewee cookbook, but found no solution so far.
So, I have the following models to four of my DB tables:
class games_def(Model):
id = PrimaryKeyField()
name = TextField()
class Meta:
database = dbmgr.DB
class users_def(Model):
id = PrimaryKeyField()
first_name = TextField()
last_name = TextField()
class Meta:
database = dbmgr.DB
class sessions(Model):
id = PrimaryKeyField()
game = ForeignKeyField(games_def, related_name = 'sessions')
user = ForeignKeyField(users_def, related_name = 'sessions')
comment = TextField()
class Meta:
database = dbmgr.DB
class world_states(Model):
session = ForeignKeyField(sessions)
time_step = IntegerField()
world_state = TextField()
class Meta:
database = dbmgr.DB
Using these models I connect to an SQLite3 DB via peewee, which works fine.
After the connection has been established I do the following in my main Python code:
models.world_states.create(session = 1, time_step = 1)
However, this gives me the following error:
sqlite3.OperationalError: table world_states has no column named session_id
That is basically correct, the table world_state does indeed not contain such a column.
However, I cannot find any reference to "session_id" in my code at all.
Whe does peewee want to use that "session_id" colum name?
Do I miss something essentially here?
When you specify a ForeignKeyField() peewee expects to use a column ending in _id based on their own name. Your wold_states.session field thus results in an column named session_id.
You can override this by setting db_column for that field:
class world_states(Model):
session = ForeignKeyField(sessions, db_column='session')

Categories