[Fixed]-How to handle manytomany field with Scrapy

1👍

https://github.com/DevProfi/scrapy-djangoitem
to handle with scrapy I use pipeline

class ItemPersistencePipeline(object):
    def process_item(self, item, spider, partial=True):
        try:
            item_model = item_to_model(item)
        except TypeError:
            return item
        model, created = get_or_create(item_model, spider.unique_fields)

        # Если объект модели не создана значит она уже есть и нужно обновить ее
        if not created:
            try:
                update_model(destination=model, source=item_model, item=item, fields=spider.unique_fields, partial=partial)
            except Exception as e:
                return e

        # Объект модели создан, нужно создать m2m объекты для нее если существуют
        else:
            item_fields_m2m = sorted(item._model_fields_m2m)
            for f in item_fields_m2m:
                val = item.get(f)
                if val:
                    getattr(model, f).set(val)
        #             TODO add bulk insert model fields
        # model.related_set.set(new_list)
        return item


def update_model(destination, source, item, fields, partial, commit=False):
    # partial включено ли частичное обновление
    # commit испольщзуется для анализа изменился ли объект чтобы зря не сохранять его в базу
    pk = destination.pk
    opts = source._meta
    fields_m2m = sorted(opts.many_to_many)
    field_names_m2m = [f.name for f in fields_m2m]
    source_fields = fields_for_model(source, exclude=field_names_m2m)

    for key in source_fields.keys():
        # if key != 'name':
        val_old = getattr(destination, key)
        t = type(val_old)
        try:
             val_new = (getattr(source, key))
        except ObjectDoesNotExist:
            continue
        if partial:
            if val_new:
                if val_new != val_old:
                    setattr(destination, key, val_new)
                    commit = True
        else:
            commit = True
            setattr(destination, key, val_new)
    if not pk:
        setattr(destination, 'pk', pk)

    if commit:
        destination.save()

    # TODO fix for update m2m fields with list
    item_fields_m2m = sorted(item._model_fields_m2m)

    for f in item_fields_m2m:
        val_new = item.get(f)
        val_old = list(getattr(destination, f).all())
        if val_new and (val_new not in val_old):
            getattr(destination, f).add(val_new)

    return destination

Leave a comment