malisit
malisit

Reputation: 1258

Django ManyToManyField in a custom template?

I'm trying to create a custom object adding template. I did the title, description etc. part but I could not figure out the manytomanyfield thing.

This is my views:

def add_product(request):
    form = ProductForm(request.POST or None)
    categories = Category.objects.all()
    if form.is_valid():
        product = form.save(commit=False)
        product.user = request.user

        at = slugify(form.cleaned_data['title'])
        if at in sluglist:
            n = 0
            while at in sluglist:
                n += 1
                at = slugify(form.cleaned_data['title'])+str(n)
            product.slug = at
            sluglist.append(product.slug)
            product.active = True
            product.save()
            return HttpResponseRedirect('/products/%s'%(product.slug))
        else:
            product.slug = slugify(form.cleaned_data['title'])
            sluglist.append(product.slug)
            product.active = True
            product.save()
            return HttpResponseRedirect('/products/%s'%(product.slug))


    return render_to_response("products/add.html", locals(), context_instance=RequestContext(request))

This is my models:

class Category(models.Model):
    title = models.CharField(max_length=120)
    description = models.CharField(max_length=500)
    slug = models.SlugField()
    timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated = models.DateTimeField(auto_now_add=False, auto_now=True)

    def __unicode__(self):
        return str(self.title)

    class Meta:
        verbose_name = "Category"
        verbose_name_plural = "Categories"



class Product(models.Model):
    user = models.ForeignKey(User, null=True, blank=True)
    title = models.CharField(max_length=50)
    description = models.TextField(max_length=500)
    price = models.DecimalField(max_digits=20, decimal_places=2)
    categories = models.ManyToManyField(Category)
    slug = models.SlugField()
    order = models.IntegerField(default=0)
    timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated = models.DateTimeField(auto_now_add=False, auto_now=True)
    active = models.BooleanField(default=True)

    def __unicode__(self):
        return str(self.title)

    class Meta:
        ordering = ['-order']

    def get_absolute_url(self, ):
        return reverse('single_product', args=[self.slug])

This is my admin.py:

class CategoryAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ('title',)}
    class Meta:
        model = Category

admin.site.register(Category, CategoryAdmin)




class ProductAdmin(admin.ModelAdmin):
    list_display = ('__unicode__','description','price','order','live_link')

    inlines = [TagInline, ProductImageInline]
    search_fields = ['title','description','price', 'tag__tag']
    list_filter = ['price', 'timestamp', 'updated']
    prepopulated_fields = {"slug": ('title',)}

    readonly_fields = ['live_link', 'timestamp', 'updated']

    class Meta:
        model = Product


    def live_link(self,obj):
        link = "<a href='/products/" + str(obj.slug)+ "/'>" + obj.title + "<a/>"
        return link

    live_link.allow_tags = True

admin.site.register(Product, ProductAdmin)

This is my forms.py:

class ProductForm(ModelForm):
    categories = forms.ModelMultipleChoiceField
    class Meta:
        model = Product
        fields = ('title', 'description', 'price', 'categories')

and this is my add.html(adding thing)

<form method='POST' action='' >
{% csrf_token %}
    <div class="form-group">
                <input type="text" placeholder="Başlık" class="form-control" name="title" value="" >
    </div>
    <div class="form-group">
                <textarea type="text" placeholder="Description" class="form-control" name="description" value="" ></textarea>
    </div>
    <div class="form-group">
                <input type="text" placeholder="Price" class="form-control" name="price" value="" >
    </div>

    <div class="form-group">
        <select multiple="multiple" name="categories" id="id_categories">
            {% for cat in categories %}
            <option>{{ cat }}</option>
            {% endfor %}
        </select>
    </div>
<input type='submit' value='Kaydet/Save' class="btn btn-info"/>

</form>

Without categories part in forms.py and add.html it works but I want to add category selection too. When I add category part that I include in codes above, it does not save the form and does not add an object. How can I solve this problem. Thanks.

Upvotes: 1

Views: 1496

Answers (1)

MBrizzle
MBrizzle

Reputation: 1813

The tag in gets its value not from the Elements between the tag (which populate the menu), but from the 'value' attribute in the tag itself. You need to change this line:

<option>{{ cat }}</option>

to this:

<option value="{{cat.id}}">{{cat}}</option>

The ModelChoiceField (the formfield used by ManyToMany objects) is expecting a model id number (although you can choose to use another model field with the 'to_field_name' attribute). Without the 'value' attribute in the option tag, the ModelChoiceField never receives a valid value, which is why the model never saves.

See the documentation on ModelChoiceFields for more information.

Upvotes: 1

Related Questions