tutorial registros pages page listar lista example crear administrador python django django-models duplicates

python - pages - listar registros django



Duplicar instancias de modelo y sus objetos relacionados en Django/Algorithm para duplicar recursivamente un objeto (10)

Tengo modelos para Books , Chapters y Pages . Todos están escritos por un User :

from django.db import models class Book(models.Model) author = models.ForeignKey(''auth.User'') class Chapter(models.Model) author = models.ForeignKey(''auth.User'') book = models.ForeignKey(Book) class Page(models.Model) author = models.ForeignKey(''auth.User'') book = models.ForeignKey(Book) chapter = models.ForeignKey(Chapter)

Lo que me gustaría hacer es duplicar un Book existente y actualizarlo a otra persona. La cuestión es que también me gustaría duplicar todas las instancias de modelos relacionadas con el Book , ¡todos sus Chapters y Pages también!

Las cosas se vuelven realmente complicadas cuando se mira una Page , no solo las nuevas Pages deben tener su campo de author actualizado sino que también deberán señalar los nuevos objetos del Chapter .

¿Admite Django una forma original de hacer esto? ¿Cómo sería un algoritmo genérico para duplicar un modelo?

Aclamaciones,

John

Actualizar:

¡Las clases dadas arriba son solo un ejemplo para ilustrar el problema que estoy teniendo!


Manera simple no genérica

Las soluciones propuestas no me funcionaron, así que fui simple, no inteligente. Esto solo es útil para casos simples.

Para un modelo con la siguiente estructura

Book |__ CroppedFace |__ Photo |__ AwsReco |__ AwsLabel |__ AwsFace |__ AwsEmotion

esto funciona

def duplicate_book(book: Book, new_user: MyUser): # AwsEmotion, AwsFace, AwsLabel, AwsReco, Photo, CroppedFace, Book old_cropped_faces = book.croppedface_set.all() old_photos = book.photo_set.all() book.pk = None book.user = new_user book.save() for cf in old_cropped_faces: cf.pk = None cf.book = book cf.save() for photo in old_photos: photo.pk = None photo.book = book photo.save() if hasattr(photo, ''awsreco''): reco = photo.awsreco old_aws_labels = reco.awslabel_set.all() old_aws_faces = reco.awsface_set.all() reco.pk = None reco.photo = photo reco.save() for label in old_aws_labels: label.pk = None label.reco = reco label.save() for face in old_aws_faces: old_aws_emotions = face.awsemotion_set.all() face.pk = None face.reco = reco face.save() for emotion in old_aws_emotions: emotion.pk = None emotion.aws_face = face emotion.save() return book


Aquí hay una manera fácil de copiar su objeto.

Básicamente:

(1) establezca el id de su objeto original en Ninguno:

book_to_copy.id = Ninguna

(2) cambie el atributo ''autor'' y guarde el efecto:

book_to_copy.author = new_author

book_to_copy.save ()

(3) INSERT realizado en lugar de ACTUALIZAR

(No aborda el cambio del autor en la Página - Estoy de acuerdo con los comentarios sobre la reestructuración de los modelos)


Creo que también estarías más feliz con un modelo de datos más simple.

¿Es realmente cierto que una Página está en algún Capítulo pero es un libro diferente?

userMe = User( username="me" ) userYou= User( username="you" ) bookMyA = Book( userMe ) bookYourB = Book( userYou ) chapterA1 = Chapter( book= bookMyA, author=userYou ) # "me" owns the Book, "you" owns the chapter? chapterB2 = Chapter( book= bookYourB, author=userMe ) # "you" owns the book, "me" owns the chapter? page1 = Page( book= bookMyA, chapter= chapterB2, author=userMe ) # Book and Author aggree, chapter doesn''t?

Parece que tu modelo es demasiado complejo.

Creo que estarías más feliz con algo más simple. Solo estoy adivinando esto, ya que no conozco todo el problema.

class Book(models.Model) name = models.CharField(...) class Chapter(models.Model) name = models.CharField(...) book = models.ForeignKey(Book) class Page(models.Model) author = models.ForeignKey(''auth.User'') chapter = models.ForeignKey(Chapter)

Cada página tiene una autoría distinta. Cada capítulo, entonces, tiene una colección de autores, al igual que el libro. Ahora puede duplicar Libro, Capítulo y Páginas, asignando las Páginas clonadas al nuevo Autor.

De hecho, es posible que desee tener una relación de varios a varios entre Página y Capítulo, lo que le permite tener múltiples copias de solo la Página, sin clonar el libro y el Capítulo.



El uso del fragmento de CollectedObjects anterior ya no funciona, pero se puede hacer con la siguiente modificación:

from django.contrib.admin.util import NestedObjects from django.db import DEFAULT_DB_ALIAS

y

collector = NestedObjects(using=DEFAULT_DB_ALIAS)

en lugar de CollectorObjects


En Django 1.5 esto funciona para mí:

thing.id = None thing.pk = None thing.save()


Esto ya no funciona en Django 1.3 ya que CollectedObjects fue eliminado. Ver el conjunto de cambios 14507

Publiqué mi solución en Django Snippets. Se basa en gran medida en el código django.db.models.query.CollectedObject utilizado para eliminar objetos:

from django.db.models.query import CollectedObjects from django.db.models.fields.related import ForeignKey def duplicate(obj, value, field): """ Duplicate all related objects of `obj` setting `field` to `value`. If one of the duplicate objects has an FK to another duplicate object update that as well. Return the duplicate copy of `obj`. """ collected_objs = CollectedObjects() obj._collect_sub_objects(collected_objs) related_models = collected_objs.keys() root_obj = None # Traverse the related models in reverse deletion order. for model in reversed(related_models): # Find all FKs on `model` that point to a `related_model`. fks = [] for f in model._meta.fields: if isinstance(f, ForeignKey) and f.rel.to in related_models: fks.append(f) # Replace each `sub_obj` with a duplicate. sub_obj = collected_objs[model] for pk_val, obj in sub_obj.iteritems(): for fk in fks: fk_value = getattr(obj, "%s_id" % fk.name) # If this FK has been duplicated then point to the duplicate. if fk_value in collected_objs[fk.rel.to]: dupe_obj = collected_objs[fk.rel.to][fk_value] setattr(obj, fk.name, dupe_obj) # Duplicate the object and save it. obj.id = None setattr(obj, field, value) obj.save() if root_obj is None: root_obj = obj return root_obj


No lo he probado en django pero la deepcopy de deepcopy podría funcionar para ti

EDITAR:

Puede definir el comportamiento de copia personalizado para sus modelos si implementa funciones:

__copy__() and __deepcopy__()


Si solo hay un par de copias en la base de datos que está compilando, he descubierto que puede usar el botón Atrás en la interfaz de administración, cambiar los campos necesarios y guardar la instancia nuevamente. Esto me ha funcionado en casos en los que, por ejemplo, necesito crear un cóctel de "gimlet" y un "vodka gimlet" donde la única diferencia es reemplazar el nombre y un ingrediente. Obviamente, esto requiere un poco de previsión de los datos y no es tan poderoso como anular la copia / copia profunda de django, pero puede ser el truco para algunos.


esta es una edición de http://www.djangosnippets.org/snippets/1282/

Ahora es compatible con el recopilador que reemplazó a CollectedObjects en 1.3.

Realmente no probé esto demasiado, pero lo probé con un objeto con aproximadamente 20,000 subobjetos, pero en solo tres capas de profundidad de clave externa. Úselo bajo su propio riesgo, por supuesto.

Para el tipo ambicioso que lee esta publicación, debe considerar la creación de subcategorías de Collector (o copiar toda la clase para eliminar esta dependencia en esta sección no publicada de la API django) a una clase llamada algo así como "DuplicateCollector" y escribir un método .duplicate que funcione de manera similar al método .delete. eso resolvería este problema de una manera real.

from django.db.models.deletion import Collector from django.db.models.fields.related import ForeignKey def duplicate(obj, value=None, field=None, duplicate_order=None): """ Duplicate all related objects of obj setting field to value. If one of the duplicate objects has an FK to another duplicate object update that as well. Return the duplicate copy of obj. duplicate_order is a list of models which specify how the duplicate objects are saved. For complex objects this can matter. Check to save if objects are being saved correctly and if not just pass in related objects in the order that they should be saved. """ collector = Collector({}) collector.collect([obj]) collector.sort() related_models = collector.data.keys() data_snapshot = {} for key in collector.data.keys(): data_snapshot.update({ key: dict(zip([item.pk for item in collector.data[key]], [item for item in collector.data[key]])) }) root_obj = None # Sometimes it''s good enough just to save in reverse deletion order. if duplicate_order is None: duplicate_order = reversed(related_models) for model in duplicate_order: # Find all FKs on model that point to a related_model. fks = [] for f in model._meta.fields: if isinstance(f, ForeignKey) and f.rel.to in related_models: fks.append(f) # Replace each `sub_obj` with a duplicate. if model not in collector.data: continue sub_objects = collector.data[model] for obj in sub_objects: for fk in fks: fk_value = getattr(obj, "%s_id" % fk.name) # If this FK has been duplicated then point to the duplicate. fk_rel_to = data_snapshot[fk.rel.to] if fk_value in fk_rel_to: dupe_obj = fk_rel_to[fk_value] setattr(obj, fk.name, dupe_obj) # Duplicate the object and save it. obj.id = None if field is not None: setattr(obj, field, value) obj.save() if root_obj is None: root_obj = obj return root_obj

EDITAR: eliminó una declaración de "impresión" de depuración.