DRF: DataFrame を使って CSV エクスポート

DRF: DataFrame を使って CSV エクスポート CSV レンダラー 1 2 3 4 5 6 7 def dataframe_to_csv_stream(df, encoding=None, **kwargs): buffer = BytesIO() df.to_csv(buffer, **kwargs) buffer.seek(0) if encoding: return BytesIO(buffer.read().decode("utf8").encode(encoding)) return buffer 1 2 3 4 5 6 7 8 9 10 class DataFrameCSVRenderer(BaseRenderer): media_type = "text/csv" format = "csv" def render(self, data, media_type=None, renderer_context=None, writer_opts=None): renderer_context = renderer_context or {} if not isinstance(data, pd.DataFrame): data = pd.DataFrame(data) stream = dataframe_to_csv_stream(data, index=False) return stream.read().decode("utf-8-sig") DataFrame を返すリストシリアライザ 1 2 3 4 5 6 7 class DataFrameSerializer(serializers.ListSerializer): @property def data(self): ret = super().data ser = ret.serializer.child columns = dict((name, field.label) for name, field in ser.fields.items()) return pd.DataFrame(ret).rename(columns=columns) エクスポートするシリアライザを生成するメタクラス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ExpporSerializerMetaclass(serializers.SerializerMetaclass): EXPORT_META = {} FIELD_CLASS_DEFAULTS = {} @classmethod def create_fields(cls): def _create_field(item): label, name, klass = item defaults = cls.FIELD_CLASS_DEFAULTS[klass] return (name, getattr(serializers, klass)(label=label, **defaults)) return dict(map(_create_field, cls.EXPORT_META)) @classmethod def annotate(cls, queryset): raise NotImplementedError() def __new__(cls, name, bases, attrs, **kwargs): attrs["Meta"].fields = [i[1] for i in cls.EXPORT_META] # CSVで返答するカラム attrs["Meta"].list_serializer_class = DataFrameSerializer # リストシリアライザ attrs.update(**cls.create_fields()) attrs["annotate"] = cls.annotate # クエリセットをアノテートさせるクラスメソッド return super().__new__(cls, name, bases, attrs, **kwargs) 実際のメタクラス 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class IncomeExpporSerializerMetaclass(ExpporSerializerMetaclass): # (ラベル, フィールド名, フィールドクラス名) EXPORT_META = [ ("会社コード", "order__company_code", "CharField"), .... ] # フィールドクラスのデフォルト値 FIELD_CLASS_DEFAULTS = dict( CharField=dict(read_only=True), DecimalField=dict(max_digits=12, decimal_places=1, read_only=True), ) @classmethod def annotate(cls, queryset): """ クエリセットを適切にアノテートする""" def _ann(item): label, name, klass = item if name.startswith("order__"): # リレーション return (name, F(name)) if name.startswith("meme__"): # 固定値 return (name, Value("")) # その他はモデルフィールドなのでアノテートしない return (None, None) ann = dict(filter(lambda i: i[1] is not None, map(_ann, cls.EXPORT_META))) return queryset.annotate(**ann) 実際のシリアライザ 1 2 3 class IncomeExportSerializer(serializers.ModelSerializer, metaclass=IncomeExpporSerializerMetaclass): class Meta: model = models.Income API 本体(ビューセット) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class IncomeViewSet(VS.BaseModelViewSet): serializer_class = serializers.IncomeSerializer queryset = models.Income.objects.all() pagination_class = paginations.Pagination renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (DataFrameCSVRenderer,) def get_serializer_class(self): """ export アクションでシリアライザを切り替える """ res = { "export": serializers.IncomeExportSerializer, }.get(self.action, super().get_serializer_class()) return res def get_queryset(self): """ export アクションの場合、必要なアノテーションを行う """ qs = super().get_queryset() if self.action == "export": qs = serializers.IncomeExportSerializer.annotate(qs).filter(level=0) return qs return qs @decorators.action(methods=["get"], detail=False) def export(self, request, pk=None): ## リスト処理を行うと シリアライザとレンダラで勝手にCSVを返答する(Accept: text/csvの時) return self.list(request)

2023年8月22日 · 2 分

MySQL BULK INSERT

MySQL バルクインサート MySQL 複数データのバルク INSERT や CSV ファイルから高速インポートする方法 【MySQL】LOAD DATA INFILE するときのファイルの文字コード 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @main.command() @click.argument("source_file") @click.option("--encoding", "-e", default="cp932") @click.pass_context def data_import(ctx, source_file, encoding): """ import TSV file """ opt = get_model()._meta fields = ",".join([f.name for f in opt.fields if f.name not in ["id"]]) SQL = f""" LOAD DATA LOCAL INFILE '{source_file}' INTO TABLE {opt.db_table} CHARACTER SET {encoding} FIELDS TERMINATED BY '\t' ({fields}) ; """ with connection.cursor() as cursor: cursor.execute(SQL) print(cursor.fetchall()) 重複時に UPDATE する(REPLACE): ...

2023年8月15日 · 2 分

MySQL only_full_group_by

only_full_group_by エラー: 1 2 3 4 5 6 OperationalError: ( 1055, "Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'server.sales_sales.sales_status' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by") 対処: sql_mod に traditional を指定する ...

2023年6月30日 · 1 分

MySQL 半角カナ検索

utf8mb4_0900_ai_ci 「utf8mb4_0900_ai_ci」という照合順序は、MySQL 8.0 から標準になった照合順序で、Unicode 9.0 の文字セットをサポートしています。¹ この照合順序は、文字列の比較において、アクセントや大小文字を区別せずに比較することができます。¹ また、日本語のようなマルチバイト文字を含む文字列を扱う場合にも適しています。¹¹: MySQL 8.0 の照合順序で標準になった「utf8mb4_0900_ai_ci」とは - Qiita ソース: Bing との会話 2023/5/31 (1) MySQL 8.0 の照合順序で標準になった「utf8mb4_0900_ai_ci …. https://qiita.com/seltzer/items/8b5d8a61591e72715d5b. (2) MySQL :: MySQL 8.0 リファレンスマニュアル :: 10.5 …. https://dev.mysql.com/doc/refman/8.0/ja/charset-applications.html. (3) MySQL :: MySQL 8.0 リファレンスマニュアル :: 10.4 接続文字 …. https://dev.mysql.com/doc/refman/8.0/ja/charset-connection.html. 半角カナの濁点ありは照合順序の設定では解決できない utf8mb4_0900_ai_ci での検索: where 結果 ベル ヘル, ベル, ヘル (つまり、 ベル は一致しない) ヘル ヘル, ベル, ヘル (つまり、 ベル は一致しない) ヘル ヘル, ベル, ヘル (つまり、 ベル は一致しない) ベル ベル (のみ) django-filter: method フィルターで対応 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from functools import reduce from operator import or_ import django_filters as DF import jaconv def filter_icontains_mix(queryset, name, value): """全角半角を区別しないで検索(濁点対応)""" if not value: return queryset values = set([jaconv.h2z(value), jaconv.z2h(value)]) def _query(v): return Q(**{f"{name}__icontains": v}) query = reduce(or_, map(_query, values)) return queryset.filter(query) class OrderFilter(DF.FilterSet): ... customer_name__contains = DF.CharFilter( field_name="customer_name", lookup_expr="icontains", method=filter_icontains_mix ) ...

2023年5月31日 · 1 分

MySQL: ERROR 1553 (HY000): Cannot drop index 'uniq_profit': needed in a foreign key constraint

MySQL: ERROR 1553 (HY000): Cannot drop index ‘uniq_profit’: needed in a foreign key constraint MySQL で一意制約が削除できない 1 2 3 UNIQUE KEY `uniq_profit` (`sales_id`,`accounting_status`), CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`), CONSTRAINT `costs_profit_chk_1` CHECK ((`fiscal_year` >= 0)) 1 2 ALTER TABLE `costs_profit` DROP FOREIGN KEY `costs_profit_sales_id_d56dec15_fk_sales_sales_id`, DROP INDEX `uniq_profit`; ALTER TABLE `costs_profit` ADD CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`); 実行: 1 2 3 4 5 6 7 mysql> ALTER TABLE `costs_profit` DROP FOREIGN KEY `costs_profit_sales_id_d56dec15_fk_sales_sales_id`, DROP INDEX `uniq_profit`; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> ALTER TABLE `costs_profit` ADD CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`); Query OK, 16 rows affected (0.17 sec) Records: 16 Duplicates: 0 Warnings: 0 Django Migration の失敗 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('partners', '0003_auto_20230426_0908'), ] operations = [ migrations.RemoveConstraint( model_name='partneruserrole', name='uniq_partneruserrole', ), migrations.AddConstraint( model_name='partneruserrole', constraint=models.UniqueConstraint(fields=('partner', 'partneruser', 'permission'), name='uniq_partneruserrole'), ), ] 制約 検索 1 2 3 4 5 6 7 8 9 SELECT DISTINCT A.CONSTRAINT_NAME, A.TABLE_NAME, A.COLUMN_NAME, B.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as A JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as B ON A.TABLE_NAME = B.TABLE_NAME WHERE A.TABLE_NAME = 'partners_partneruserrole' AND B.CONSTRAINT_NAME like 'uniq_%' AND A.CONSTRAINT_NAME not like 'uniq_%' AND A.CONSTRAINT_NAME != 'PRIMARY'; +-----------------------------------------------------------+--------------------------+----------------+----------------------+ | CONSTRAINT_NAME | TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME | +-----------------------------------------------------------+--------------------------+----------------+----------------------+ | partners_partneruser_partneruser_id_d59f67cc_fk_partners_ | partners_partneruserrole | partneruser_id | uniq_partneruserrole | | partners_partneruser_partner_id_852ab185_fk_partners_ | partners_partneruserrole | partner_id | uniq_partneruserrole | | partners_partneruser_permission_id_031e3996_fk_auth_perm | partners_partneruserrole | permission_id | uniq_partneruserrole | +-----------------------------------------------------------+--------------------------+----------------+----------------------+ 再設定 1 2 3 4 5 6 7 ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partneruser_id_d59f67cc_fk_partners_`; ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partner_id_852ab185_fk_partners_`; ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partner_id_852ab185_fk_partners_`, DROP INDEX `uniq_profit`; ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` FOREIGN KEY (`partneruser_id`) REFERENCES `partners_partneruser` (`id`); ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_partner_id_852ab185_fk_partners_` FOREIGN KEY (`partner_id`) REFERENCES `partners_partner` (`id`); ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_permission_id_031e3996_fk_auth_perm` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`user_ptr_id`); 最終スキーマ 1 2 3 4 5 6 7 8 9 10 11 12 DROP TABLE IF EXISTS `partners_partneruserrole`; CREATE TABLE `partners_partneruserrole` ( `id` bigint NOT NULL AUTO_INCREMENT, .... PRIMARY KEY (`id`), UNIQUE KEY `uniq_partneruserrole` (`partner_id`,`partneruser_id`,`permission_id`), KEY `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` (`partneruser_id`), KEY `partners_partneruser_permission_id_031e3996_fk_auth_perm` (`permission_id`), CONSTRAINT `partners_partneruser_partner_id_852ab185_fk_partners_` FOREIGN KEY (`partner_id`) REFERENCES `partners_partner` (`id`), CONSTRAINT `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` FOREIGN KEY (`partneruser_id`) REFERENCES `partners_partneruser` (`user_ptr_id`), CONSTRAINT `partners_partneruser_permission_id_031e3996_fk_auth_perm` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2023年4月26日 · 2 分

MySQL: ERROR 3780 (HY000) at line 1406622: Referencing column 'os_branch_id' and referenced column 'id' in foreign key constraint '***' are incompatible.

ERROR 3780 (HY000) at line 1406622: Referencing column 'os_branch_id' and referenced column 'id' in foreign key constraint '***' are incompatible. 1 2 3 4 5 6 7 8 9 10 11 CREATE TABLE `gas_bombehouse` ( `id` int NOT NULL AUTO_INCREMENT, `code` varchar(50) NOT NULL, .... `os_branch_id` bigint DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `code` (`code`), KEY `gas_bombehouse_os_branch_id_d35d17d3_fk_outsource` (`os_branch_id`), CONSTRAINT `gas_bombehouse_os_branch_id_d35d17d3_fk` FOREIGN KEY (`os_branch_id`) REFERENCES `outsources_outsourcebranch` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3; /*!40101 SET character_set_client = @saved_cs_client */; 1 2 3 4 5 6 7 CREATE TABLE `outsources_outsourcebranch` ( `id` bigint NOT NULL AUTO_INCREMENT, ... PRIMARY KEY (`id`), ) ENGINE=InnoDB AUTO_INCREMENT=9751 DEFAULT CHARSET=utf8mb3; /*!40101 SET character_set_client = @saved_cs_client */; 原因 リモートの mysqldump を ローカルにそのまま取り込もうとしていた 古いスキーマが残っていて、そのidがint であったため。 DROP TABLE IF EXISTS table CREATE TABLE table が実行されるまで、古いスキーマが残っている、ということ。 1 2 3 4 5 DROP TABLE IF EXISTS `outsources_outsourcebranch`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `outsources_outsourcebranch` .... 解決 DROP DATABASE & CREATE DATABASE して、更地にmysqldumpをロードする 資料 How to fix MySQL error 1215 Cannot add foreign key constraint ...

2023年4月6日 · 2 分

MySQL: JSON_OBJECT: JSON文字列フィールドを含む時のバックスラッシュ問題

how to replace double back slash character to single one from MySQL JSON_OBJECT function results for column including JSON string You can use the MySQL function REPLACE() to replace double backslashes with single backslashes in a JSON object. Here’s an example: 1 SELECT REPLACE(JSON_OBJECT(column_name), '\\\\\\\\', '\\\\') FROM table_name; This will return the column values with double backslashes replaced with single backslashes. If you want to insert a JSON object literal with single backslashes instead of double backslashes, you can set the NO_BACKSLASH_ESCAPES SQL mode⁴. When this mode is set, a single backslash instead of double backslashes can be used to insert the JSON object literal, and the backslashes are preserved⁴. If you use the JSON_OBJECT() function when performing the insert and this mode is set, you must alternate single and double quotes⁴. ...

2023年3月28日 · 2 分

Snowfake: ビュー

Snowflake View ビューの概要 CREATE VIEW マテリアライズドビュー(Enterprise Edition) マテリアライズドビューの使用 ベーステーブルで実行された DML の量に関係なく、マテリアライズドビューを介してアクセスされるデータは常に最新です。 マテリアライズドビューが最新になる前にクエリが実行されると、Snowflakeはマテリアライズドビューを更新するか、 マテリアライズドビューの最新部分を使用して、必要な新しいデータをベーステーブルから取得します。 CREATE MATERIALIZED VIEW ビューとマテリアライズド・ビューの違いを理解する Snowflake マテリアライズドビューについて SnowflakeのMaterialized View マテリアライズドビューとそのベーステーブルをクラスタリングするためのベストプラクティス PostgreSQL マテリアライズドビューの機能(PostgreSQL) マテリアライズドビューの内容をテーブルの最新の状態と同じにしたいときには、REFRESH MATERIALIZED VIEWを実行します MySQL MySQLでMaterialized Viewを実現する 残念ながら、MySQLではデフォルトでMaterialized Viewを作る機能は存在しません。 トリガーかバッチで参照用のテーブルを作る

2023年3月18日 · 1 分

Snowflake: NDJSON format

CREATE FILE FORMAT https://docs.snowflake.com/ja/sql-reference/sql/create-file-format JSON https://docs.snowflake.com/ja/sql-reference/sql/create-file-format#type-json -- If TYPE = JSON COMPRESSION = AUTO | GZIP | BZ2 | BROTLI | ZSTD | DEFLATE | RAW_DEFLATE | NONE DATE_FORMAT = '<string>' | AUTO TIME_FORMAT = '<string>' | AUTO TIMESTAMP_FORMAT = '<string>' | AUTO BINARY_FORMAT = HEX | BASE64 | UTF8 TRIM_SPACE = TRUE | FALSE NULL_IF = ( '<string>' [ , '<string>' ... ] ) FILE_EXTENSION = '<string>' ENABLE_OCTAL = TRUE | FALSE ALLOW_DUPLICATE = TRUE | FALSE STRIP_OUTER_ARRAY = TRUE | FALSE STRIP_NULL_VALUES = TRUE | FALSE REPLACE_INVALID_CHARACTERS = TRUE | FALSE IGNORE_UTF8_ERRORS = TRUE | FALSE SKIP_BYTE_ORDER_MARK = TRUE | FALSE

2023年2月14日 · 1 分

doctrine ORM

$ ./composer.phar require doctrine/orm 2.4.* ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing doctrine/collections (dev-master 3661cd8) Cloning 3661cd8bc5152598dbe6772e98b78d1fa8281a67 - Installing doctrine/cache (dev-master e7b77e5) Cloning e7b77e5abe230a2cc1db5005fb86435da213ae3b - Installing doctrine/inflector (dev-master b0b2fef) Cloning b0b2feffb47906a03b570777c07044c529d1d124 - Installing doctrine/common (2.5.x-dev 26727ba) Cloning 26727ba78de21a824dcbfa5a8ab52c21fe7d71d5 - Installing doctrine/dbal (dev-master cc2d503) Cloning cc2d50385efa1ea87f5d0afa2c47cc2499914f81 - Installing doctrine/orm (2.4.x-dev 0cf7e0e) Cloning 0cf7e0e628c1409c9235c9b107c9623a2e8a80ef doctrine/orm suggests installing symfony/yaml (If you want to use YAML Metadata Mapping Driver) Writing lock file Generating autoload files

2015年8月6日 · 1 分