djang-mptt: tree 構造で export する

django-mptt: Tree モデルを エクスポート mptt_tags テンプレートライブラリ の recursetree / endrecursetree タグを使う profiles/templates/profile/workgroup/tree.json: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 {% load mptt_tags %} [ {% recursetree nodes %} { "id": "{{ node.id }}", "code": "{{ node.code|default:''}}", "name": "{{ node.name }}", "path": "{{ node.path }}", "full_name": "{{ node.full_name }}", "data": {}, "children": [{{ children }}] }{% if node.get_next_sibling %},{% endif %} {% endrecursetree %} ] 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 django.template import engines, loader from django.utils.safestring import mark_safe from django.db.models import QuerySet def render(src, request=None, engine_name="django", safe=True, **ctx): text = engines[engine_name].from_string(src).render(ctx, request=request) return safe and mark_safe(text) or text def render_by(name, request=None, safe=True, **ctx): t = loader.get_template(name) text = t.render(ctx, request=request) return safe and mark_safe(text) or text def export_trees(nodes, model_class=None, template_name=None, request=None, safe=True, **ctx): if not model_class and isinstance(nodes, QuerySet): model_class = nodes.model if not template_name and model_class: template_name = f"{model_class._meta.app_label}/{model_class._meta.model_name}/tree.json" if not template_name: return "" return render_by(template_name, request=request, safe=safe, nodes=nodes, **ctx) 1 2 3 4 5 6 7 8 @main.command() @click.argument("path") @click.pass_context def workgroup_export(ctx, path): """ Workgroup Export """ nodes = models.Workgroup.objects.all() print(export_trees(nodes))

2023年5月4日 · 1 分

Django: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async

Jupyter: Django: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async このエラーは、asyncコンテキストから同期的なコードを呼び出そうとした場合に発生するエラーです。 async-unsafeなコードを呼び出す場合は、asyncコンテキストからではなく、自分自身の同期関数で書き、それをasgiref.sync.syncを使用して呼び出すように修正する必要があります¹。 ¹: Django: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. - Stack Overflow ソース: Bing との会話 2023/5/4 (1) Django: SynchronousOnlyOperation: You cannot call this …. https://stackoverflow.com/questions/61926359/django-synchronousonlyoperation-you-cannot-call-this-from-an-async-context-u. (2) django.core.exceptions.SynchronousOnlyOperation after …. https://github.com/django/channels/issues/1464. (3) python - Django channels Async Websocket throwing Error …. https://stackoverflow.com/questions/63892884/django-channels-async-websocket-throwing-error-while-trying-to-use-database-quer. (4) Django channel occasionally gets error “cannot call this from …. https://stackoverflow.com/questions/69239657/django-channel-occasionally-gets-error-cannot-call-this-from-an-async-context. (5) python - Getting SynchronousOnlyOperation error Even after …. https://stackoverflow.com/questions/63149616/getting-synchronousonlyoperation-error-even-after-using-sync-to-async-in-django. DJANGO_ALLOW_ASYNC_UNSAFE 環境変数を使用することで、非同期コンテキストで SynchronousOnlyOperation エラーが発生した場合に警告を無効にすることができます¹。 ...

2023年5月4日 · 1 分

factory_boy: SubFactory

factory_boy: SubFuctory で ForeignKeyフィールドを初期化する factory_boy はテストデータを簡単に作るためのライブラリです²。ForeignKey フィールドのインスタンスのデフォルトを定義するには、SubFactory を使う方法があります¹⁴。例えば、以下のように書けます。 1 2 3 4 5 6 7 8 class PhoneContactFactory(factory.django.DjangoModelFactory): class Meta: model = models.PhoneContact class CoopFactory(factory.django.DjangoModelFactory): class Meta: model = models.Coop phone = factory.SubFactory(PhoneContactFactory) この場合、CoopFactory を使って Coop インスタンスを作ると、PhoneContactFactory も使って PhoneContact インスタンスを作り、そのインスタンスを Coop の phone フィールドにセットします。 もしくは、SelfAttribute を使う方法もあります⁴。例えば、以下のように書けます。 1 2 3 4 5 6 7 8 class SubtitlesFactory(factory.django.DjangoModelFactory): class Meta: model = models.Subtitles class RecordingFactory(factory.django.DjangoModelFactory): class Meta: model = models.Recording subtitles = factory.SubFactory(SubtitlesFactory, language=factory.SelfAttribute('..language')) この場合、RecordingFactory を使って Recording インスタンスを作るときに language パラメータを指定すると、その値が SubtitlesFactory の language パラメータにも渡されます。 ...

2023年4月30日 · 2 分

DRF: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list

DRF: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list ViewSetの queryset で order_by を明示的に指定すること I see. Django REST framework also supports pagination for large result sets, but you need to specify a default pagination class and a page size in your settings.py file¹. Alternatively, you can set the pagination_class attribute for each viewset individually⁴. However, you still need to order your queryset by some field, otherwise you will get the same warning as before²³. You can use the order_by method on your queryset or the ordering attribute on your viewset¹. For example: ...

2023年4月29日 · 1 分

Django MPTT: values で tree_id が SELECT されてしまう

Django MPTT: values で tree_id が SELECT されてしまう 仕様である django-mptt の TreeQuerySet で values コールすると SQL に tree_id が追加されているのは、django-mptt がツリー構造を管理するために必要なカラムだからです²。tree_id は、同じツリーに属するノードに同じ値を持ちます²。例えば、以下のようなモデルがあるとします。 1 2 3 4 5 6 7 8 9 from django.db import models from mptt.models import MPTTModel, TreeForeignKey class Category(MPTTModel): name = models.CharField(max_length=50, unique=True) parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') class MPTTMeta: order_insertion_by = ['name'] このとき、以下のように values をコールすると、SQL に tree_id が追加されます²。 ...

2023年4月28日 · 1 分

Django: Template: QuerySetの指定したindexのインスタンスの参照

Django: Template: QuerySet の指定した index のインスタンスの参照 配列ではないので、数値でインデクシングできない {{ e.expenseitem_set.0 }} {# NG #} 1件目, 最後 {{ e.expenseitem_set.first.account_name }} {{ e.expenseitem_set.last.account_name }} テンプレートフィルター 1 2 3 4 5 6 7 from django import template register = template.Library() @register.filter def index(queryset, i): return queryset[i] 1 {% load expenses %} {{ e.expenseitem_set|index:1 }} QuerySet のインクシング list のように -1 などの Python のインデクシングはできない (するには list(queryset) で list に変換する)

2023年4月27日 · 1 分

django: has_permが 常に Falseになる

has_perm が常にFalseになる ## 無効ユーザーだから(User.is_active == False) django.contrib.auth.backends.py: 1 2 3 4 class BaseBackend: def has_perm(self, user_obj, perm, obj=None): # ユーザーが有効の時だけパーミッション判定する return user_obj.is_active and super().has_perm(user_obj, perm, obj=obj) キャッシュがのこっているため Userオブジェクトをクエリし直す

2023年4月26日 · 1 分

django-mptt: tree_idの重複問題

tree_id が重複して登録されていまう問題 多重処理を行っている時に発生することがある模様 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class TreeManager(models.Manager.from_queryset(TreeQuerySet)): ... @delegate_manager def insert_node(self, node, target, position='last-child', save=False, allow_existing_pk=False, refresh_target=True): if node.pk and not allow_existing_pk and self.filter(pk=node.pk).exists(): raise ValueError(_('Cannot insert a node which has already been saved.')) if target is None: tree_id = self._get_next_tree_id() setattr(node, self.left_attr, 1) setattr(node, self.right_attr, 2) setattr(node, self.level_attr, 0) setattr(node, self.tree_id_attr, tree_id) setattr(node, self.parent_attr, None) ... def _get_next_tree_id(self): max_tree_id = list(self.aggregate(Max(self.tree_id_attr)).values())[0] max_tree_id = max_tree_id or 0 return max_tree_id + 1 ... ロックをかける django-mptt で tree_id の採番に関して、テーブルロックをかけることは可能です。django-mptt は、デフォルトでは、tree_id の採番にテーブルロックをかけていませんが、以下のようにして、テーブルロックをかけることができます³。 ...

2023年4月25日 · 1 分

Django: User: Permissionコードのどれかを保有しているユーザーを検索

Django: User: Permission コードのどれかを持っているユーザーを検索 1 2 3 from operator import or_ from functools import reduce from django.db.models import Q 1 2 3 4 5 6 7 8 9 10 11 12 13 class UserQuerySet(models.QuerySet): def filter_has_perm(self, perm_codeset: List[str]): """ Permission code(List[app_label.codename]) の一覧のどれかを含むユーザーの検索 """ def _query(item): return reduce(or_, [ Q(user_permissions__content_type__app_label=item[0], user_permissions__codename=item[1]), Q(groups__permissions__content_type__app_label=item[0], groups__permissions__codename=item[1]), ]) query = reduce(or_, map(_query, map(lambda i: i.strip().split("."), perm_codeset))) return self.filter(query) 1 2 3 4 5 6 7 8 9 10 11 12 13 class UserFilter(django_filters.FilterSet) def filter_permission_codes__in_csv(self, queryset, name, value): if not value: return queryset values = value.split(",") if isinstance(value, str) else value if len(values) < 1: return queryset return queryset.filter_has_perm(values) permission_codes__in_csv = DF.BaseInFilter( label="パーミッションコード(CSV)", method="filter_permission_codes__in_csv", )

2023年4月23日 · 1 分

Djanog: Permission: app_lablel.codename

Django: Permission: app_label.codename をアノテートする 1 2 3 from django.db.models import F, Value from django.db.models.functions import Concat from django.contrib.auth import models as auth_models 1 2 3 4 5 6 7 class PermissionDef: @classmethod def choices(cls): return auth_models.Permission.objects.annotate( code=Concat("content_type__app_label", Value("."), "codename") ).values_list("code", "name") ただし、件数が大きいので, フィールドのchoices に使うのは現実的ではない

2023年4月23日 · 1 分