Django ModelForm with ManyToManyField wont save to DB - django

I`m using Django 1.9 with the following:
views.py:
def a_new(request):
# submit button
if request.method == "POST":
form = AForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('ui:config-list')
# first visit
else:
form = AForm()
template = 'api/test_template.html'
context = RequestContext(request, {'form': form})
return render_to_response(template, context)
forms.py:
class AForm(forms.ModelForm):
b= forms.ModelMultipleChoiceField(
queryset=B.objects.all(),
widget=FilteredSelectMultiple("b", is_stacked=False)
)
class Meta:
model = A
fields = ('name', 'b', )
test_template.html
...
<form role="form" class="form-inline" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{ form.name }}
{{ form.b }}
</div>
<div class="dl-horizontal">
<button type="submit" class="btn btn-success">Save</button>
</div>
</form>
models.py:
class A(models.Model):
name = models.CharField(max_length=100)
b = models.ManyToManyField(B, null=True, blank=True, name='b')
...
The problem is that when I use the Save button - only the name field is created in the item. The b item, that the widget is being used on, is just not being saved to the DB with the new item A created(it does show error if I leave the b field empty so I guess its checking it too).
What can be the problem?

See the documentation about using commit=False when you have a many-to-many field.
Note that there is no reason for you to be using commit=False here though. Remove it, and the second save, and Django will save your values directly.

b is ManyToManyField, it does on saved on the table A. In a different table the relation between A and B is maintained.

If you have an M2M field and do save(commit=False) you need to call this at the end:
form.save_m2m()
See the docs.

Related

How to get a Django ModelForm to redirect to another ModelForm on submit?

I have a template which displays a certain ModelForm at one of the URLs of my Django site. The ModelForm is based on a model with two fields,
class ActionCode(models.Model):
action_code = models.CharField(blank=False, max_length=10,
verbose_name="Action Code")
description = models.TextField(blank=True)
class Meta:
unique_together = ('action_code',)
I would like my ModelForm to give the user only the first field (Action Code), then when it is submitted, verify if the value entered already exists or not. If it doesn't already exist, I want to redirect the user to be able to enter a Description for that Action Code (the second field in my model). So I wrote a second ModelForm which uses the Description field of the model, and I would like my first form to redirect to the second form after validating that the data is new. Ideally then, the Description would be linked to this specific piece of data and both would go into the same Django database table (hence them coming from the same model). However, when I enter a new piece of data and hit Submit, the site simply stays at the /action_code/ URL and displays a Submit button, and nothing else. How do I get the first form to redirect to the second form, and what is the best way to make sure it only does so if the data is new?
Here are my urls.py:
url(r'^action_code/', action_code_form, name="actioncode"),
url(r'descr_form/', code_description_form, name='description'),
Here are my two forms in `forms.py':
class ActionCodeForm(ModelForm):
class Meta:
model = ActionCode
fields = ('action_code',)
class ActionCodeDescriptionForm(ModelForm):
class Meta:
model = ActionCode
fields = ('description',)
Here is my views.py:
def action_code_form(request):
form = ActionCodeForm()
description_form = ActionCodeDescriptionForm
if request.method == 'POST':
form = ActionCodeForm(request.POST)
if form.is_valid():
action_code = form.cleaned_data['action_code']
form.save()
return render(request, 'description_form.html',
{'form': description_form})
return render(request, 'action_code_form.html', {'form': form})
def code_description_form(request):
form = ActionCodeDescriptionForm()
if request.method == 'POST':
form = ActionCodeDescriptionForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'description_form.html', {'descr_form': form})
And my templates:
action_code_form.html:
<form method="post" action="">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit"/>
</form>
description_form.html:
<form method="post" action="">
{% csrf_token %}
<table>
{{ descr_form }}
</table>
<input type="submit" value="Submit"/>
</form>
Forms don't redirect to anything. It's the view's responsibility to do the redirect.
You need two views, with two URLs; when the first form submission is successful, the view should redirect to the second URL.

CreateView and related model fields with fixed inital values

I'm working with a CreateView where I know what some of the field values will be ahead of time. In the example below, I know that the author field for a new Entry object will be the current user and I use get_initial() to preset this.
Now I would like to omit this field from my template form. I've tried several approaches:
Simply commenting out the author field in the form template leads to an invalid form.
Leaving 'author' out of fields. Nope.
And here's a related problem. The example below involves a relationship to a User object that exists. But what if I need to create an object, say an auth Group for editors? I've tried creating a placeholder group and renaming it ... and, well, that didn't work very well.
#
# model
#
class Author(Model):
name = CharField(max_length = 60)
user = OneToOneField(to = User, related_name = 'author_user', on_delete = CASCADE)
class Entry(Model):
title = CharField(max_length = 60)
author = ForeignKey(to = Author, related_name = 'entry_author')
#
# view
#
class EntryCreateView(CreateView):
model = Entry
fields = ('title', 'author')
def get_initial(self):
initial = super(EntryCreateView, self).get_initial()
initial['author'] = get_object_or_404(Author, user = self.request.user)
return initial
#
# template
#
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
<label for="{{ form.author.id_for_label }}">Author:</label>
{{ form.author }}
<p>
<input type="submit" class="btn btn-primary" name="save" value="Save" />
<input type="submit" class="btn btn-primary" name="cancel" value="Cancel" />
</form>
{% endblock %}
You can manually set user in form_valid() method of EntryCreateView class:
class EntryCreateView(CreateView):
model = Entry
fields = ('title',)
def form_valid(self, form):
user = self.request.user
form.instance.user = user
return super(EntryCreateView, self).form_valid(form)
You'll need to create a ModelForm for the customizations you need (https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/).
You can't remove author because it's required on your model currently.
Try something like this:
In forms.py...
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['title', 'author']
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
self.author = initial.get('author')
super(EntryForm, self).__init__(*args, **kwargs)
You can make modifications to the fields (set to not required, delete a field from the form fields, etc) in __init__ or on the class.
Just import and reference this form in your views to use it.

Django - Display imagefield in ManytoMany form instead of title

I am working on a Django project with crispy forms.
I want to use images instead of the the default Models title/label to select a instance in a Many to Many relation form.
Content models.py:
class Cloth(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=200)
picture = ImageCropField(upload_to='cloth_pics/%Y-%m-%d/',
blank=True)
def __str__(self):
return self.title
class Outfit(models.Model):
owner = models.ForeignKey('profiles.Profile')
title = models.CharField(max_length=200)
cloths=models.ManyToManyField(Cloth)
Content forms.py
class ClothForm(forms.ModelForm):
class Meta:
model = Cloth
fields = ('title','type','picture')
class OutfitForm(forms.ModelForm):
class Meta:
model = Outfit
exclude= ["owner"]
Content views.py
def outfits_new(request):
if request.method == "POST":
form = OutfitForm(request.POST)
if form.is_valid():
outfit = form.save(commit=False)
outfit.owner = get_user(request)
outfit.created_date = timezone.now()
outfit.save()
pk=outfit.id
return HttpResponseRedirect(reverse('outfit_edit_delete', args=[pk]))
else:
cloths = Cloth.objects.filter(owner=request.user.id)
form = OutfitForm()
return render(request, '../templates/outfits_new.html', {'form': form, "cloths":cloths})
Content outfits_new.html
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form|crispy }}
<div class="btn-group" role="group" aria-label="Basic example">
<input type="submit" value="Submit" name="edit" class="btn btn-success">
</div>
This code produces a Outfit form where I can select different cloths( displaying the cloths title). I want to select different cloths using a image from the cloths.picture field.
Thank you very much,
Patrick
Have a look at select2 at https://select2.github.io/examples.html. It allows you to do images in comboboxes
There is a Django package at https://github.com/applegrew/django-select2

Django form is not valid

I am trying to submit a form and it will not pass as is_valid in the view.
The form uses forms.Modelforms which I do not have a lot of experience with.
I checked {{ form.errors }} in the template but didn't get anything back.
I appreciate the time and expertise
Form
class AddSubcategory(forms.ModelForm):
class Meta:
model = Subcategory
fields = ['category']
subcategory_name = forms.CharField(max_length=255)
View
#login_required
#locationed
def add_subcategory(request, location):
subcat_form = AddSubcategory(request.POST)
if subcat_form.is_valid():
submitted_subcat_name = subcat_form.cleaned_data['subcategory_name']
selected_cat = subcat_form.cleaned_data['category']
_, was_created = Subcategory.objects.get_or_create(name=submitted_subcat_name, category=selected_cat)
return HttpResponseRedirect(reverse('manage_cat_subcat', args=(location.slug,)))
else:
cat_form = AddCategory()
subcat_form = AddSubcategory()
return render(request, 'inventory/manage_cat_subcat.html', {'location': location,'cat_form': cat_form,'subcat_form':subcat_form})
Template (form)
<form class="form-inline" action="{% url 'add_subcategory' location.slug %}" method="post">
{% csrf_token %}
{{subcat_form.category}}
{{subcat_form.subcategory_name}}
<button class="btn btn-small" type="submit">Add Subcategory</button>
</form>
You specify in fields that you need id, category and name but you don't put them in your form in your template.
You have only category and subcategory_name.
You can add those two elements in your template OR remove them from the fields list.
Also you don't specify an action for your form, you should give the view where the data from your form should be sent.

Django imageField not validating - Why?

I have a model:
class PartnerPrefs(models.Model):
partner = models.ForeignKey(Partner)
theme = models.IntegerField()
email = models.EmailField()
logo_file = models.ImageField(upload_to='logos/')
And my forms:
class PartnerPrefsForm(ModelForm):
theme = forms.ChoiceField(
choices=THEME_CHOICE,
widget=forms.Select(),
initial='1',
)
class Meta:
model = PartnerPrefs
exclude = ('partner',)
And my views:
...
if request.method == 'POST':
prefsform = PartnerPrefsForm(request.FILES, request.POST)
if prefsform.is_valid():
# do stuff
And finally my template:
<form enctype='multipart/form-data' form action='.' method='post'>
{% csrf_token %}
{{ prefsform.as_p }}
<input type="submit" value="Submit" />
</form>
Whenever I submit the form all field come back with This field is required.... If I eliminate the ImageField from the form, then it works fine. I cannot find what the problem is.
The issue turned out to be the order in which request.POST and request.FILES are passed to the from. request.POST must go first.