2017-06-10 9 views
6

Mam model Django (o nazwie BiomSearchJob), który jest obecnie aktywny i chcę dodać nową relację wiele do wielu, aby system był bardziej dostosowywany dla użytkownika. Wcześniej użytkownicy mogli przesłać zadanie bez określenia zestawu TaxonomyLevelChoices, ale aby dodać więcej funkcji do systemu, użytkownicy powinni mieć teraz możliwość wyboru własnych poziomów systematyki.Retroaktywnie ustawić nowe wartości domyślne ManyToManyField na istniejący model

Oto model:

class TaxonomyLevelChoice(models.Model): 
    taxon_level = models.CharField(
     verbose_name="Taxonomy Chart Level", max_length=60) 
    taxon_level_proper_name = models.CharField(max_length=60) 

    def __unicode__(self): 
     return self.taxon_level_proper_name 

class BiomSearchJob(models.Model): 
    ... 
    # The new many-to-many relation 
    taxonomy_levels = models.ManyToManyField(
     'TaxonomyLevelChoice', blank=False, max_length=3, 
     default=["phylum", "class", "genus"]) 

    name = models.CharField(
     null=False, blank=False, max_length=100, default="Unnamed Job", 
     validators=[alphanumeric_spaces]) 
    ... 

Obecnie wszystkie istniejące BiomSearchJobs niejawnie mają trzy poziomy taksonomii wymienione w perspektywie default= (które nie są wybierane przez użytkownika), a więc wszystkie są takie same w bazie danych. Po uruchomieniu migrate stwierdzam, że poprzednie zadania nie mają od razu trzech relacji poziomu taksonomii, zwracają tylko pusty zestaw po wywołaniu job.taxonomy_levels.all() (jeśli job były instancją BiomSearchJob).

Czy istnieje sposób retroaktywnego dodania tego związku bez ręcznego przechodzenia przez wszystko? Idealnie, po prostu uruchamiając migrate chciałbym istniejące BiomSearchJobs mieć phylum, class iwymienionych w atrybucie taxonomy_levels.

Odpowiedz

7

Wydaje mi się, że poszukujesz migracji danych, która jest migracją umożliwiającą zmiany danych tylko w bazie danych.

Można tworzyć to w ten sposób:

python manage.py makemigrations <your app> --name=retroactively_add_levels 

następnie wstaw ten kod do pliku migracji tylko Utworzono:

# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 

from django.db import migrations 


def add_taxonomy_levels(apps, schema_editor): 
    BiomSearchJob = apps.get_model('<your app>', 'BiomSearchJob') 
    TaxonomyLevelChoice = apps.get_model('<your app>', 'TaxonomyLevelChoice') 
    for job in BiomSearchJob.objects.all(): 
     for choice in TaxonomyLevelChoice.objects.filter(taxon_level_proper_name__in=["phylum", "class", "genus"]): 
      job.taxonomy_levels.add(choice) 

class Migration(migrations.Migration): 

    dependencies = [] 

operations = [ 
    migrations.RunPython(add_taxonomy_levels, reverse_code=migrations.RunPython.noop) 
] 

działa całkiem dużo jak kwerendy SQL zrobi ale wykorzystuje on Django ORM.

Mam nadzieję, że to pomoże.

3

Twoje podejście nie działa, ponieważ nieodwracalnie chcesz utworzyć zapytanie dotyczące właściwości instancji: Django nie może tego odgadnąć.

Od Django's Doc, domyślnie może być funkcją, natomiast

Dla takich dziedzinach jak ForeignKey że mapować do modelowania instancji, domyślnie powinna być wartość pola one odniesienia (pk chyba to_field jest ustawiony), zamiast modelu instancje.

Więc .... Albo przekazujesz tablicę PK (np. ID), albo używasz funkcji, aby uzyskać zestaw zapytań.

class TaxonomyLevelChoice(models.Model): 
    taxon_level = models.CharField(
     verbose_name="Taxonomy Chart Level", max_length=60) 
    taxon_level_proper_name = models.CharField(max_length=60) 

    def __unicode__(self): 
     return self.taxon_level_proper_name 

def get_default_taxonomy_levels(): 
    ... 
    return YourQuerySet 


class BiomSearchJob(models.Model): 
    ... 
    # The new many-to-many relation 
    taxonomy_levels = models.ManyToManyField(
     'TaxonomyLevelChoice', blank=False, max_length=3, 
     default=get_taxonomy_levels_default) 

    name = models.CharField(
     null=False, blank=False, max_length=100, default="Unnamed Job", 
     validators=[alphanumeric_spaces]) 
    ... 

Chyba trzeba będzie zagadnień związanych z migracją, jeśli migrować przed TaxonomyLevelChoice przypadkach nie została utworzona.

Poszedłbym do rozwiązania funkcji z powodu poprzedniego zdania, z pewną metodą pseudo pamięci podręcznej, ponieważ tworzenie zapytania za każdym razem, gdy tworzysz BiomSearchJob, nie jest akceptowalnym rozwiązaniem.

zrobiłbym:

DEFAULT_TAXONOMY_LEVELS = None 

def get_default_taxonomy_levels(): 
    if DEFAULT_TAXONOMY_LEVELS: 
     return DEFAULT_TAXONOMY_LEVELS 
    ... 
    DEFAULT_TAXONOMY_LEVELS = YourQuerySet 
    return YourQuerySet 

Edycja: Ponieważ pytanie jest retro aktywnie ustawić wiele do wielu wartości domyślnej, którą proponujemy polecenie, aby to zrobić na istniejących instancji, jak don Sądzę, że migracja poradzi sobie z tym za Ciebie.

Powiązane problemy