Как экспортировать в Excel объект Django, где каждый объект ForeignKey - новый столбец, с помощью либы импорта-экспорта?
У меня есть модель, представляющая некоторый товар на складе:
(Код был упрощен)
class StockItem(models.Model):
name = models.CharField()
category = models.Charfield()
stock = models.ForeignKey(Stock) # <- Here is few stocks
quantity = models.IntegerField(default=0)
Таким образом, каждый StockItem может существовать на разных складах, но с разным количеством остатков. Есть ли способ создать ModelResource для экспорта оставшегося количества для каждого запаса?
Пример таблицы:
+----+--------+----------+---------+---------+
| ID | Name | Category | Stock A | Stock B |
+----+--------+----------+---------+---------+
| 1 | Item 1 | Cat 1 | 1 | 1 |
| 2 | Item 2 | Cat 2 | 10 | 8 |
| 3 | Item 3 | Cat 3 | 14 | 32 |
+----+--------+----------+---------+---------+
Мой ресурс ModelResource:
class StockItemsRemainingsResource(resources.ModelResource):
class Meta:
model = models.StockItem
fields = ('name', 'category')
def __init__(self):
super().__init__()
for item in self.get_queryset():
stock_name = item.stock.name
self.fields[f"quantity_{stock_name}"] = fields.Field(
attribute="quantity",
column_name=stock_name,
widget=widgets.ForeignKeyWidget(
models.Stock,
"pk"
)
)
Я пытался переопределить метод __init__
, но, вероятно, сделал это неправильно.
Метод after_export()
можно переопределить, чтобы изменить набор данных после первоначального экспорта.
Вы можете добавить в набор данных столбец по мере необходимости.
Например, вы можете добавить что-то вроде этого метода в StockItemsRemainingsResource
:
def after_export(self, queryset, dataset, **kwargs):
counts = []
for d in dataset.dict:
stock_count = Stock.objects.filter(d["name"]).count()
counts.append(stock_count)
dataset.append_col(counts, header="count")
Вы можете поиграть с этим, чтобы найти лучший способ сделать это. Это будет неэффективно, поэтому, вероятно, не будет хорошо работать с большими наборами данных.
См. документацию по таблицам .
После нескольких часов поисков я смог добиться такой структуры, переопределив export_field()
после изучения кода некоторых библиотек.
В моем случае мне пришлось сначала создать обязательные поля, переопределив метод __init__
, но используя обычный класс fields.Field
для подготовки этих полей к ручному заполнению в методе export_field()
:
def __init__(self):
super().__init__()
stocks = models.Stock.objects.all()
for stock in stocks:
field_name = f'stock_{stock.id}'
self.fields[field_name] = fields.Field(
column_name=f'{stock.name}',
attribute=field_name
)
Последний шаг, который я сделал, это переопределение export_field(self, field, instance)
, которое берет каждый экземпляр на итерации queryset к каждому полю ресурса для обработки. В моем случае я проверил имя поля, и если оно совпадает с одним из полей акций, то я подсчитаю q-количество экземпляров, относящихся к конкретной акции, и верну его (в противном случае верну super()
).
Мой код :
def export_field(self, field, instance):
# overrided to populate dynamic fields (stock quantity)
# If field is stock ("stock_{id}") then extract and ID for querying
# an actual quantity
if field.attribute.startswith("stock"):
stock_id = field.attribute.split("_")[-1]
stock = models.Stock.objects.filter(pk=stock_id).first()
return instance.get_stock_quantity(stock) if stock else None
else:
return super().export_field(field, instance)
Возможно, это не лучший подход, и у меня есть предположение, что его можно реализовать, используя dehydrate_method
, который может быть назначен экземпляру field.Field(dehydrate_method=some_method())
, но я не искал решения в этом направлении.