I have a ModelChoiceField in a form that uses a TextInput widget. I want to be able to select a value from the database or add new entries to the database with this input. If the value is not already in the database, I get an error on the form that says "Select a valid choice. That choice is not one of the available choices."
Model
class FeedCategory(models.Model):
category = models.CharField(max_length=255, unique=True)
class RssFeed(models.Model):
category = models.ForeignKey(FeedCategory, null=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=255)
feed = models.URLField()
Form
class RssForm(forms.Form):
name = forms.CharField()
feed = forms.URLField()
category = forms.ModelChoiceField(queryset=FeedCategory.objects.all(), to_field_name='category', widget=forms.TextInput())
def clean(self):
cleaned_data = super().clean()
????
Views
class RssCreateView(FormView):
template_name = 'dashboard/rss_feed_form.html'
form_class = RssForm
success_url = '/dashboard/'
def form_valid(self, form):
name = form.cleaned_data['name']
feed = form.cleaned_data['feed']
category = form.cleaned_data['category']
rss_obj = RssFeed(category=category, name=name, feed=feed)
rss_obj.save()
return super().form_valid(form)
Template
<form method="post">
{%csrf_token%}
{{form|crispy}}
<button type="submit">Save</button>
</form>
It might help you what I am using:
category = models.ForeignKey("General.entity",verbose_name='Category', db_column="CategoryEntityRef", null=False, blank=False)
so, what I am doing with this is creating a field that points to an existing category that exists in another table. It will display it as a dropdown box. However using this method will allow me to have the option to add another Category:
Related
I want to create Django Forms to save transactions for the store. I want a final template similar to this. The ER Diagram for my database is also shown in the image.
My Django Models are:
class Party(models.Model):
party_id = models.BigAutoField(primary_key=True)
party_name = models.CharField(max_length=128)
party_phone = models.CharField(max_length=128)
party_address = models.CharField(max_length=128)
def __str__(self):
return self.party_name
class Items(models.Model):
item_no = models.BigAutoField(primary_key=True)
item_type = models.BooleanField(default=True)
item_name = models.CharField(max_length=128)
item_qty = models.PositiveIntegerField(default=0)
item_cost = models.PositiveIntegerField(default=0)
def __str__(self):
return self.item_name
class Sales(models.Model):
invoice_no = models.BigAutoField(primary_key=True)
invoice_date = models.DateField(default=date.today)
party = models.ForeignKey(Party, on_delete=models.CASCADE)
def __str__(self):
return str(self.invoice_no)
class SalesTransaction(models.Model):
sales = models.ForeignKey(Sales, on_delete=models.CASCADE)
items = models.ForeignKey(Items, on_delete=models.CASCADE)
purchase_qty = models.PositiveIntegerField(default=0)
total_cost = models.PositiveIntegerField(default=0)
def __str__(self):
return self.item_name
I can achieve this with AJAX but I don't think it is the best solution because lots of validations have to be made with hardcoding.
How can I create Django Form to save data in that multiple tables in a single Save Click?
I have been looking for a way to do the same thing recently. The solution I stumbled upon was to use two form objects under a single <form> tag. You can differentiate between the form objects by giving them prefix attributes before passing to context. For example:
def get_view(request):
....
form1 = Form1(prefix="form1")
form2 = Form2(prefix="form2")
return render(request, 'template.html', {'form1': form1, 'form2': form2})
In your template, you would call them as usual under a single form tag:
<form>
{{ form1.as_p }}
{{ form2.as_p }}
<input type='submit'>
</form>
Now in your post view, you can use the prefix to get data of each form individually:
def post_view(request):
form1 = Form1(request.POST, prefix="form1")
form2 = Form2(request.POST, prefix="form2")
....
I am trying to let a user create a "club" (basically a group) where the user later on can add users to.
Currently it does not create a field in the database somehow.
Any suggestions would be appreciated since I am fairly new to forms.
Model
class Club(models.Model):
owner = models.CharField(max_length=30)
topic = models.CharField(max_length=30)
start = models.DateTimeField(verbose_name='start date', auto_now_add=False)
end = models.DateTimeField(verbose_name='end date', auto_now_add=False)
account = models.ManyToManyField(Account)
Views
#login_required
def add_club(request):
if request.method == "POST":
form = AddClubForm(request.POST, instance=request.user)
print(form)
if form.is_valid():
form.save()
return HttpResponseRedirect(request.path_info)
else:
form = AddClubForm(instance=request.user)
return render(request, 'page/club.html', {
"form": form,
})
Form
class AddClubForm(forms.Model):
owner = forms.CharField(required=True)
topic = forms.CharField(required=False)
start = forms.DateField(required=False)
end = forms.DateField(required=False)
class Meta:
model = Club
fields = (
'owner',
'topic',
'start',
'end',
)
Template
<form method="POST" action="">
{% csrf_token %}
<div class="col-md-6">
<label class="labels">Create a club</label>
{{ form.owner }}
<input class="btn" type="submit" value="Add club">
</div>
</form>
Since this answered your problem, I am posting the solution here:
You need to add blank=True, null=True to your fields in your model, otherwise it expects them when saving the form.
class Club(models.Model):
owner = models.CharField(max_length=30)
topic = models.CharField(max_length=30, blank=True, null=True)
start = models.DateTimeField(verbose_name='start date', auto_now_add=False, blank=True, null=True)
end = models.DateTimeField(verbose_name='end date', auto_now_add=False, blank=True, null=True)
account = models.ManyToManyField(Account, blank=True)
Concerning the instance=request.user, I believe you misunderstanding the use of instance in a ModelForm.
If the request method is GET, the instance is used to populate a ModelForm with data from an existing Club object, and then pass it to your template to display the information.
If the request method is POST (or PUT), instance represent the existing Club object you want to update with data received from the form.
You usually need to use the instance arg in a DetailView (either to update or retrieve one specific Club), never when creating an object.
That's why you need to remove the instance arg in your views.py:
form = AddClubForm(request.POST)
I'm currently learning Django forms and I came across this post.
One of the forms currently looks like this:
What I'd like to do is to change Category into a formset and be able to render multiple dropdowns while creating a product.
My models.py:
class Category(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(decimal_places=2, max_digits=10)
category = models.ForeignKey(Category, on_delete = models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
My forms.py:
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name', )
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('name', 'price', 'category', )
def __init__(self, user, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs)
self.fields['category'].queryset = Category.objects.filter(user=user)
Current method in views.py:
#login_required
def new_product(request):
if request.method == 'POST':
form = ProductForm(request.user, request.POST)
if form.is_valid():
product = form.save(commit=False)
product.user = request.user
product.save()
return redirect('products_list')
else:
form = ProductForm(request.user)
return render(request, 'products/product_form.html', {'form': form})
products_form.html:
{% extends 'base.html' %}
{% block content %}
<h1>New product</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="save">
cancel
</form>
{% endblock %}
What I tried is to make use of the modelformset_factory and change the method in views.py by creating a CategoryFormSet as:
CategoryFormSet = modelformset_factory(Category, fields=('name', ), extra=2)
formset = CategoryFormSet(data=data, queryset=Category.objects.filter(user=request.user))
then replacing the original form from views.py with the created formset. In the html I simply replace the {{form}} with {{formset}}. After playing around with it for a while, I either get the New product with just a submit button (no form rendered) or a User object has no attribute GET error. What am I doing wrong?
The tutorial focuses on allowing the user to add/update more instances of one model. You want to edit one thing, with multiple related things inline.
However, your data model only allows one category per product, so this does not make any sense. Whether you want more than one category per product, is something only you can answer :) - I'm going to assume you want that.
First you need to change your model to allow for multiple categories per product:
class Product(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(decimal_places=2, max_digits=10)
categories = models.ManyToManyField(Category, related_name='products')
user = models.ForeignKey(User, on_delete=models.CASCADE)
And then you need to learn about Inline Formsets.
Come back with a specific if you get stuck on that.
Instead of creating new model Category. You can do this.
CATEGORY_CHOICES= (
("1", "1"),
("2", "2"),
("3", "3"),
("4", "4"),
("5", "5"),
("6", "6"),
("7", "7"),
("8", "8"),
)
category = models.CharField(max_length = 20,choices = CATEGORY_CHOICES,default = '1')
It will automatically render in HTML.
In my Django project I have catalog of products, there is how its look like in models.py:
class Category(models.Model):
category = models.CharField(max_length=15)
description = models.CharField(max_length = 1000)
class Product(models.Model):
name = models.CharField(max_length=15)
description = models.CharField(max_length=1000)
category = models.ForeignKey(Category)
This is product detail view.
def ProductDetailView(request, pk):
productdetail = get_object_or_404(Product, pk=pk)
return render(request, 'product/detail.html', {'productdetail':productdetail})
And this is urls.py for product detail view:
url(r'^(?P<pk>[0-9]+)/$', views.ProductDetailView, name='detail'),
Now I need to add an order button on product detail page. When user click on this button, the data with the order have to save to the database, so i made a new model:
class Order(models.Model):
order_id = models.AutoField(primary_key=True, unique=True)
customer_name = models.CharField(max_length=20)
email = models.CharField(max_length=50)
product = models.ForeignKey(Product)
All datas for this model i take from the form, but i don't know how to get current product field. I will be thankful if you will help me with it.
You can add current item ID to HTML (by template tags). For example as hidden form field:
<input id="id_current_item_id" name="current_item_id" type="hidden"
value="{{ current_item.id }}"/>
Try
<input name="prouct_id" id="id_product" type="hidden" value="{{product.id}}">
and submit the order form. You will get product id in view.
I have two models in Django that are related with a OneToOneField (PrinterProfile and PrinterAdress).
I am trying to do a form with PrinterProfileForm, but for some reason it does NOT pass the PrinterAddress fields into the form (it's not rendered by Django "magic" in the template).
What should I do so that my PrinterProfileForm include as well the fields from PrinterAddress (its related OneToOneField)?
Thanks a lot
class PrinterProfile(TimeStampedModel):
user = models.OneToOneField(User)
phone_number = models.CharField(max_length=120, null=False, blank=False)
additional_notes = models.TextField()
delivery = models.BooleanField(default=False)
pickup = models.BooleanField(default=True)
# The main address of the profile, it will be where are located all the printers.
class PrinterAddress(TimeStampedModel):
printer_profile = models.OneToOneField(PrinterProfile)
formatted_address = models.CharField(max_length=200, null=True)
latitude = models.DecimalField(max_digits=25, decimal_places=20) # NEED TO CHECK HERE THE PRECISION NEEDED.
longitude = models.DecimalField(max_digits=25, decimal_places=20) # NEED TO CHECK HERE THE PRECISION NEEDED.
point = models.PointField(srid=4326)
def __unicode__(self, ):
return self.user.username
class PrinterProfileForm(forms.ModelForm):
class Meta:
model = PrinterProfile
exclude = ['user']
You have to create second form for PrinterAddress and handle both forms in you view:
if all((profile_form.is_valid(), address_form.is_valid())):
profile = profile_form.save()
address = address_form.save(commit=False)
address.printer_profile = profile
address.save()
Of course in the template you need to show both forms under one <form> tag :-)
<form action="" method="post">
{% csrf_token %}
{{ profile_form }}
{{ address_form }}
</form>
Complementing the accepted answer:
If you have custom clean methods, you need to add a try/except case. For the example presented if address had a clean() method to validate something you needed to change it to:
def clean(self):
try:
printer_profile = self.printer_profile
except ObjectDoesNotExist:
pass
else:
...code to validate address...