Show values in dropdown list in Django - django

I want to add documents about different parts of my facility in my Django app.
So, I have the following models in my models.py:
class Parts(models.Model):
Name = models.CharField(max_length=50)
class Docs(models.Model):
Date = models.DateField(default=date.today)
Type = models.CharField(max_length=50)
Part = models.ForeignKey(Parts)
Link = models.FileField(upload_to='Docs/%Y/%m/%d')
forms.py:
class DocsForm(ModelForm):
class Meta:
model = Docs
fields = ['Date', 'Type', 'Part', 'Link']
class PartsForm(ModelForm):
class Meta:
model = Parts
fields = ['Name']
views.py:
def adddocs(request):
if request.method == 'POST':
f = DocsForm(request.POST, request.FILES)
if f.is_valid():
f.save()
return HttpResponseRedirect('')
else:
form = DocsForm()
return render(
request,
'adddocs.html',
{'form': form}
and the following fragment in my template:
<form action="{% url 'adddocs' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p> {{form.Date}} </p>
<p> {{form.Type}} </p>
<p> {{form.Part}} </p>
<p> {{form.Link}} </p>
<p><input type="submit" value="Add" /></p>
</form>
And everything works fine except one problem. Now I have two parts of my facility, for example 'tubes' and 'storage'. But if I want to choose them in dropdown list, I see the following variants in my browser:
Parts Object
Parts Object
What should I change to see names of parts like this
tubes
storage
?

You need the str() method in your Models.
The str() method is called whenever you call str() on an object. Django uses str(obj) in a number of places. Most notably, to display an object in the Django admin site and as the value inserted into a template when it displays an object. Thus, you should always return a nice, human-readable representation of the model from the str() method.
class Parts(models.Model):
Name = models.CharField(max_length=50)
# call to return name
def __str__(self):
return self.Name

Related

Django get data of object from ManyToManyField form

Hello i created this form :
class CartForm(ModelForm):
class Meta:
model = Cart
fields =( 'products',)
from theses models :
class Product(models.Model):
title = models.CharField("Titre", max_length=120)
subtitle = models.CharField("Sous-titre", max_length=250)
description = models.TextField("Description")
picture = models.ImageField(upload_to='objects/')
enabled = models.BooleanField("Activé")
class Cart(models.Model):
products = models.ManyToManyField(Product)
and i want to display on my template an list of choice with their data
So i send form from views but i don't find any way to get the products description i only get their names !
here is my view :
def home(request):
categories = Category.objects.annotate(test=Count('product')).filter(test__gt=0)
# categories = Category.objects.order_by(
# 'id')
test = CartForm()
return render(request, 'boutique.html', {"categories": categories, "test":test})
and what i tried in my template :
{% for ee in test.products %}
{{ ee.description }}
<br />
{% endfor %}
please help me
have a nice day
Ok so theres a couple issues here:
First of all you actually need to define what is gonna happen when you submit the form, so in your view do this:
views.py
def home(request):
categories = Category.objects.annotate(test=Count('product')).filter(test__gt=0)
if request.method == 'POST':
test = CartForm(request.POST)
if form.is_valid():
cart = form.save(commit=False)
for product in request.POST.getlist('products'):
cart.add(product)
else:
pass
else:
form = CartForm()
return render(request, 'boutique.html', {"categories": categories, "test": test})
then in your template you actually have to render the form (test):
boutique.html
<form method="post">
{% csrf_token %}
{{ test.as_p }}
<button type="submit"> Add Items </button>
</form>
Now you should see the list of products in template.
edit
if you want to show a different model field in your form rewrite its __str__ method like this:
def __str__(self):
return self.description # to show description

I would like to do the same in views that admin.register decorator does in admin

I would like to be able to register different models from a single front view, as I can do from the admin create view.
for example in models.py:
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL,
null=True)
in admin.py I have:
#admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
in the admin panel I get
I would like to be able to add author by the same way from the create book view in frontend, thank you for your help
For this make create modelform for Book Model in forms.py
class AddBook(forms.ModelForm):
class Meta:
model = Book
fields = ['title','author']
set url for adding the book in urls.py
path('add/book/',views.add_book,name='add_book')
In views.py
def add_book(request):
if request.method == POST:
form = AddBook(request.POST)
if form.is_valid():
book = form.save(commit=False)
book.save()
return redirect('redirect where you want to redirect')
else:
form = AddBook()
return render (request,'add_book.html',{'form':form})
add_book.html
<form action= '{% url 'add_book' %} method='post'>
{% csrf_token %}
{{ form.as_p }}
<button type = 'submit'>Submit</button>
</form>
OR you can you use the built-in class-based generic views https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/

How to create form field for every foreign key in django?

I am trying to build a form and I am not sure how this should be done correctly. These are my models:
class Country(models.Model):
name = models.CharField(max_length=250, null=False, blank=False, unique=True)
twocode = models.CharField(max_length=5, null=False, blank=False, unique=True)
class GeoBonus(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, related_name='geo_bonuses')
bookmaker = models.ForeignKey(Bookmaker, related_name='geo_bonuses')
Bookmaker has bonuses, different for each country. For example:
Bookmaker has bonuses:
Slovakia: "eligible for 100% up to $200"
Afghanistan: "eligible for 100% up to €100!"
USA: "restricted country"
...
And I want to save text in quotes as name in GeoBonus. Of course I can write use simple model form, but I would have submit form 248 time(for each country). I would like to show all fields for each country.
If name is blank, GeoBonus will not be created.
If name is not blank, create GeoBonus object.
This is what it should look like:
How code in forms.py and views.py will look like? I also need to edit fields.
I tried to manually create new fields for countries:
<form method="post" action="" class="wide">
{% csrf_token %}
{%bootstrap_form form %}
<div class="form-group">
{%for country in countries%}
<label class="control-label" for="{{country.twocode}}">{{country}}</label>
<input class="form-control" id="{{country.twocode}}" maxlength="250" type="text" />
{%endfor%}
</div>
<input type="submit" class="btn btn-default" name="submit" value="Save">
</form>
Using this forms.py class:
class GeoBonusForm(forms.ModelForm):
class Meta:
model = GeoBonus
fields = ['bookmaker']
But request.POST does contain only bookmaker field.
EDIT1: Views.py
#staff_member_required
def geo_bonus_edit(request, bookmaker=None):
template = loader.get_template('geobonus/edit.html')
if request.method == 'POST':
form = GeoBonusForm(request.POST)
print request.POST
else:
form = GeoBonusForm()
context = RequestContext(request, {
'form': GeoBonusForm,
'countries': Country.objects.all(),
})
return HttpResponse(template.render(context))
I would suggest, that you generate the fields in your form dynamically. It could look like this:
class GeoBonusForm(forms.ModelForm):
countries = Country.objects.all()
def __init__(self, *args, **kwargs):
super(GeoBonusForm, self).__init__(*args, **kwargs)
for country in self.countries:
self.fields[country.name] = forms.CharField()
This allows you to generate a CharField for every Country you have.
Therefore the saving is a bit different than the normal ModelForm would expect it I would recommend to override the save method:
def save(self, commit=True):
super(GeoBonusForm, self).save(commit=False)
bookmaker = Bookmaker.objects.get(id=self.cleaned_data['bookmaker'].id)
for field in self.cleaned_data:
if field != 'bookmaker':
country = Country.objects.get(name=field)
geo_bonus, created = GeoBonus.objects.get_or_create(bookmaker=bookmaker, country=country)
geo_bonus.name = self.cleaned_data[field]
geo_bonus.save()
At first we try to get the chosen bookmaker. After that we iterate over the cleaned fields (for more about form cleaning take a look here) and try to get_or_create the GeoBonus object. Now we just fill in the value of the corresponding country field and save.
I adapted the code of your view a little bit:
def index(request, bookmaker=None):
template = loader.get_template('geobonus/edit.html')
if request.method == 'POST':
form = GeoBonusForm(request.POST)
if form.is_valid():
form.save()
else:
form = GeoBonusForm()
context = RequestContext(request, {
'form': GeoBonusForm,
})
return HttpResponse(template.render(context))
You don't need to pass the countries to the context anymore, because we generate the field in the form. On POST we check if the form is valid and save it, if it is.
For the template you only should only need this now:
<form action="." method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
Hope I got it right and that this solves your problem.
Edit: Note, that this is a simple quick example. Of course you should clean the data and check if the input is valid and maybe prepare it for a more simple saving process.
You could use Django's inlinemodelformset for that, refer to using a formset in views and templates for example code.

django: How to use inlineformset within the formwizard?

I'm displaying two separate sample projects. The first is a Contact related and shows the principle of using the formwizard. The second is an ingredients to recipes related project which shows how to use inlines within a form. I want inlines to be in my formwizard the same way they work in a normal form.
I have a formwizard multistep form working. It is based off the example here. I've changed it slightly to use modelform.
models.py
from django.db import models
# Create your models here.
class Contact(models.Model):
subject = models.CharField(max_length=50)
sender = models.EmailField()
def __unicode__(self):
return self.subject
class Contact2(models.Model):
message = models.TextField(max_length=500)
def __unicode__(self):
return self.message
forms.py
class ContactForm1(forms.ModelForm):
class Meta:
model = Contact
class ContactForm2(forms.ModelForm):
class Meta:
model = Contact2
class ContactWizard(FormWizard):
#property
def __name__(self):
return self.__class__.__name__
def done(self, request, form_list):
# do_something_with_the_form_data(form_list)
return HttpResponseRedirect('/done/')
urls.py
(r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
Separately I have inlines being generated into another form. I'm doing this via inlineformset_factory in my view. This is not connected to the formwizard example above. This is an ingredients to recipes example.
I'm doing this like:
views.py
def add(request):
IngredientFormSet = inlineformset_factory(Recipe, Ingredient,
fk_name="recipe",
formfield_callback=curry(ingredient_form_callback, None))
if request.method == 'POST':
form = RecipeForm(request.POST)
formset = IngredientFormSet(request.POST)
if form.is_valid() and formset.is_valid():
recipe = form.save()
formset = IngredientFormSet(request.POST, instance=recipe)
formset.save()
return redirect("/edit/%s" % recipe.id)
else:
form = RecipeForm()
formset = IngredientFormSet()
return render_to_response("recipes_add.html", {"form":form, "formsets":formset}, context_instance=RequestContext(request))
recipes_add.html
<form method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<hr>
<h3>Ingredients</h3>
<div class="inline-group">
<div class="tabular inline-related last-related">
{{ formsets.management_form }}
{% for formset in formsets.forms %}
<table>
{{ formset }}
</table>
{% endfor %}
</div>
</div>
<p class="success tools">Add another row</p>
<input type="submit" value="Add">
</form>
How can I get the inlines to work within my formwizard multistep form?
The models.py now looks like this because I want books to be inlines to contact. I want the inlines to be on the first step of my formwizard. Then go through to step 2 and finish.
from django.db import models
# Create your models here.
class Contact(models.Model):
subject = models.CharField(max_length=50)
sender = models.EmailField()
def __unicode__(self):
return self.subject
class Contact2(models.Model):
message = models.TextField(max_length=500)
def __unicode__(self):
return self.message
class Book(models.Model):
author = models.ForeignKey(Contact)
title = models.CharField(max_length=100)
The formwizard included in Django (below version 1.4) doesn't support formsets. Beginning with version 1.4, there will be a much better implementation (see https://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/)
Back to your question, if you can't wait for the next Django release - which I assume - you could stick to django-formwizard. The last release (1.0) is api compatible to the upcoming Django formwizard.
With the new formwizard implementation you can use FormSets the same way you use normal Forms.

Django Form and Image Upload on same page

I'm trying to figure out how to upload an image and get user input on a single form.
My models:
class Image(models.Model):
artist = models.ForeignKey('Artist')
image = models.ImageField(upload_to="assets/images")
class Album(models.Model):
artist = models.ForeignKey(Artist,null=True)
notes = models.CharField(max_length = 50)
display = models.BooleanField()
date_created = models.DateTimeField(auto_now_add=True)
My forms
class AlbumForm(forms.ModelForm):
class Meta:
model = Album
fields = ('notes',)
class ImageForm(forms.ModelForm):
class Meta:
model = Image
exclude = ('artist')`
I think my view is wrong and how would I pass the two forms to the template? What would the template look like to render the two forms? I want to use a single submit button.
def create(request):
form1 = ImageForm(request.POST, request.FILES or None)
form2= AlbumForm(request.POST or None)
if form2.is_valid() and form1.is_valid():
image = form1.save(commit=False)
image.artist = Artist.objects.get(pk=3)
image.save()
album = form2.save(commit=False)
album.save()
if 'next' in request.POST:
next = request.POST['next']
else:
next = reverse('art_show')
return HttpResponseRedirect(next)
return render_to_response(
'art/create.html',
{'ImageForm':form1},
{ 'AlbumForm': form2},
context_instance = RequestContext(request)
)
You could probably do something like this:
<form action="." method="post" enctype="multipart/form-data">
{{ImageForm.image}} <br />
{{AlbumForm.notes}} <br />
{{AlbumForm.display}} <br />
...
<input type="submit" value="Save" />
</form>
This will return both form1 and form2 objects in your request.POST object.
views.py:
...
return render_to_response('art/create.html',
{'ImageForm': form1, 'AlbumForm': form2},
context_instance = RequestContext(request)
)
Or you could do this:
...
return render_to_response('art/create.html',
locals(),
context_instance = RequestContext(request)
)
Although, the second one will add all variables your function uses so you should make sure that if you use it that your function won't be using any builtin names. Usually uncommon, but you should just make sure.
EDIT: Added a submit button to make it clear you only need one. Also added the view's response.