Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ManyToManyField does not save in django

Tags:

python

django

I have a situation again, when I do a form.save(), my form saves only the parent table, it does not save the intermediary table which is required for Many-To-Many relationships.

My models.py look like this

class Platform(models.Model):
    id = models.AutoField(primary_key=True)
    description = models.TextField(blank=True)
    annotation_file_archived_location = models.FileField(upload_to='msrb/platform')
    anntation_file_hashsum = models.TextField()
    annotation = models.TextField(unique=True)

    def __unicode__(self):
        return self.annotation

    class Meta:
        managed = True
        db_table = 'platform'


class Dataset(models.Model):
    dataset_id = models.TextField(primary_key=True)
    title = models.TextField(blank=True, null=True)
    taxonomy = models.ForeignKey('Organism', blank=True, null=True)
    citation = models.TextField(blank=True, null=True)
    summary = models.TextField(blank=True, null=True)
    contributor = models.TextField(blank=True, null=True)  # This field type is a guess.
    submitted = models.DateField(blank=True, null=True)
    last_updated = models.DateField(blank=True, null=True)
    author = models.ForeignKey('Users', db_column='author', blank=True, null=True)
    platforms = models.ManyToManyField(Platform,through='DatasetPlatform')#,through_fields=('Platform:platform','dataset'))

    class Meta:
        managed = True
        db_table = 'dataset'

class DatasetPlatform(models.Model):
    id = models.IntegerField(primary_key=True)
    platform = models.ForeignKey(Platform,  null=False)
    dataset = models.ForeignKey(Dataset,null=False)

    class Meta:
        managed = False
        db_table = 'dataset_platform'

Forms.py

class DatasetForm(forms.ModelForm):
dataset_id = forms.CharField(required=True,help_text="dataset_id")
title = forms.CharField(required=True,help_text="title")
taxonomy = forms.ModelChoiceField(queryset=Organism.objects.all(),empty_label=None,help_text='Taxonomy')
citation = forms.CharField(required=True,help_text="citation")
summary = forms.CharField(required=True,help_text="summary")
contributor = forms.CharField(help_text="contributor (separated by comma)")
submitted = forms.DateField(initial = datetime.now,required=True,help_text="Submitted date")
last_updated = forms.DateField(initial = datetime.now,required=True,help_text="Last Updated date")
platform = forms.ModelMultipleChoiceField(queryset=Platform.objects.all(),help_text="Choose the platforms this dataset belongs to")

class Meta:
    model = Dataset
    fields = ('dataset_id','title','taxonomy','citation','summary','contributor','submitted','last_updated','platform')# Add author later ,'author')

views.py

def add_dataset(request):
context_dict = {}
if request.method == 'POST':
    form = DatasetForm(request.POST)
    if form.is_valid():
        print "------------------------------------------------------------------------------"
        print form.cleaned_data['platform']
        form.save()
        print "------------------------------------------------------------------------------"
        return HttpResponseRedirect('/msrb/')
    else:
        print form
        print form.errors
else:
    form = DatasetForm()
context_dict['form'] = form
template = get_template('msrb/add_dataset.html')
context = RequestContext(request,context_dict)
return HttpResponse(template.render(context))

I have tried saving the data using

form.save(commit=True)
 form.save_m2m()

form.cleaned_data gives the proper output. I am not sure what am I missing here as I dont get an error message from django too.

EDIT I have a workaround for the problem, but I am not sure if this is the best solution. If I can get a better solution, I will be greatful.

def add_dataset(request):
context_dict = {}
if request.method == 'POST':
    form = DatasetForm(request.POST)
    if form.is_valid():
        print form.cleaned_data['platform']
        f = form.save()
        for p in form.cleaned_data['platform']: <--- Added
            d = DatasetPlatform(dataset = f,platform = p) <--- Added
            d.save() <--- Added
        return HttpResponseRedirect('/msrb/')
    else:
        print form
        print form.errors
else:
    form = DatasetForm()
context_dict['form'] = form
template = get_template('msrb/add_dataset.html')
context = RequestContext(request,context_dict)
return HttpResponse(template.render(context))
like image 315
manugupt1 Avatar asked Jun 11 '15 14:06

manugupt1


People also ask

What does save () do in Django?

The save method is an inherited method from models. Model which is executed to save an instance into a particular Model. Whenever one tries to create an instance of a model either from admin interface or django shell, save() function is run.

Does Django objects create call save?

Does Django objects create call save? Creating objects To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the database. This performs an INSERT SQL statement behind the scenes. Django doesn't hit the database until you explicitly call save() .

How do Django fields work?

It looks through all the class attributes of your model, and any that are instances of a Field subclass it moves into a fields list. That list is assigned as an attribute of the _meta object, which is a class attribute of the model. Thus you can always get to the actual Field objects via MyModel.


1 Answers

Django is not able (well, refuses) to automatically save m2m relations with a custom through model. Saving the form data uses direct assignment to the ManyToManyField, which will not work as explained here.

If removing the custom through model is an option, I'd do that. Granted, it will have to be managed = True, but it greatly simplifies use of the field. You're not saving any extra data in the relationship, so it might be an option.

Otherwise, you have already found the only workaround. Each time you want to manipulate the m2m relationship, you'll have to manually create, alter and delete the DatasetPlatform instances. Again, this is explained in further detail in the relevant documentation.

like image 100
knbk Avatar answered Sep 23 '22 05:09

knbk