2012-04-09 3 views
7

Я новичок в django, и в качестве обучающего приложения я создаю приложение для регистрации расходов.Копирование полей ManyToMany из одного экземпляра модели в другой

В моей модели у меня есть три класса, которые выглядят, как это (я упростил их немного для краткости):

class AbstractExpense(models.Model): 
    description = models.CharField(max_length=100) 
    amount  = models.IntegerField() 
    category = models.ForeignKey('Category') 
    tags  = models.ManyToManyField('Tag') 
    insert_date = models.DateTimeField(auto_now=True) 

    class Meta(object): 
     abstract = True 

class Expense(AbstractExpense): 
    date  = models.DateField('Date') 

class RecurringExpense(AbstractExpense): 
    FREQUENCY_CHOICES = (('D', 'daily'), 
         ('W', 'weekly'), 
         ('M', 'monthly'), 
         ('Y', 'yearly')) 
    start_date = models.DateField(blank=False) 
    end_date = models.DateField(blank=True, null=True) 
    last_check = models.DateField(blank=True, null=True) 
    frequency = models.CharField(blank=False, max_length=1, choices=FREQUENCY_CHOICES) 

RecurringExpense просто шаблон: когда система понимает, что время, чтобы вставить повторяющуюся расходы (например: арендная плата), он должен получить информацию в шаблоне и скопировать их в новый экземпляр класса Expense. Вот соответствующий бит от метода RecurringExpense, ответственного за работу:

Expense(description=self.description, 
     amount=self.amount, 
     category=self.category, 
     # tags=self.tags, 
     date=expense_date).save() 

Вышеприведенные работает безупречно, но если я раскомментировать tags=self.tags линию, Джанго жалуется и бросить следующее сообщение об ошибке:

Exception Type: TypeError 
Exception Value: 'tags' is an invalid keyword argument for this function 
Exception Location: <snip>/django/db/models/base.py in __init__, line 367 

I знаю I could create a loop, чтобы обойти эту проблему, но мне интересно, есть ли более элегантный способ, который позволил бы мне выполнить то же самое сразу ....

ответ

8

Самый простой метод, который я мог придумать:

e = Expense(description=self.description, 
      amount=self.amount, 
      category=self.category, 
      date=expense_date) 
e.save() 
e.tags = self.tags.all() 
+1

Вы также можете заменить 'Expense (...)' /'e.save() 'на' Expense.objects.create (...) ' –

+0

Это может не сработать, если у вас большое количество тегов (драйвер SQL зависимый). В этом случае вы можете выполнять итерацию в больших кусках по всем тегам, чтобы добавить их. – odedfos

11

Вы не можете установить поле m2m непосредственно так, как при создании модели i nstance. Попробуйте следующие вместо:

expense = Expense(description=self.description, 
     amount=self.amount, 
     category=self.category, 
     date=expense_date) 
expense.save() 
expense.tags.add(*self.tags.all()) 

Вы можете проверить https://docs.djangoproject.com/en/1.4/topics/db/examples/many_to_many/ больше примеров о том, как работать со многими-ко-многим.

+0

Привет и спасибо за ваш ответ. Есть ли конкретная причина, по которой вы используете метод '.add()' в сочетании с распаковкой списка, а не простое назначение (см. Мой собственный ответ)? – mac

+0

Мне часто не нужно заменять существующий набор отношений m2m, просто добавляйте к нему, поэтому он больше используется для этого. Для вашего случая использования назначение действительно проще и должно работать нормально, поскольку «ManyRelatedObjectsDescriptor» выполняет '.clear' +' .add (* values) 'за кулисами при выполнении задания. –

+0

Хорошая точка. Upvoted! :) – mac