Django how to use a ModelChoiceField in a formset_factory - django

I am trying to use a modelchoicefield from the form in a formset_factory but i dont understand the error and don't know how to solve it.
(edit added the models.py)
models.py
class Order(Model):
user = ForeignKey(User, on_delete=SET_NULL, null=True)
fee = ForeignKey(Fee, on_delete=SET_NULL, null=True)
route = ForeignKey(Route, on_delete=SET_NULL, null=True)
price_rate = ForeignKey(PriceRate, on_delete=SET_NULL, null=True)
pallet_amount = IntegerField()
status = BooleanField()
postal_code = CharField(max_length=6)
city = CharField(max_length=255)
street = CharField(max_length=255)
delivery_from = DateTimeField()
delivery_until = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True, blank=True)
updated_at = DateTimeField(auto_now=True)
deleted_at = DateTimeField(null=True, blank=True)
views.py
def routecreate_view(request):
orderformset = formset_factory(OrdersRouteForm, can_delete=False, extra=1)
if request.method == 'POST':
form = RouteForm(request.POST)
formset = orderformset(request.POST)
if form.is_valid() and formset.is_valid():
# process the data in form.cleaned_data as required
messages.success(request,
"You succesfully created an route.")
return HttpResponseRedirect(reverse('planner.dashboard'))
else:
form = RouteForm()
formset = orderformset()
return render(request, 'planner/route.html', {'form': form, 'formset': formset})
forms.py
class OrdersRouteForm(forms.ModelForm):
route = ModelChoiceField(
queryset=Order.objects.filter(status=1, delivery_until__gte=datetime.datetime.now(), deleted_at=None),
label='Order')
class Meta:
model = Order
fields = ("route",)
def __init__(self, *args, **kwargs):
super(OrdersRouteForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control m-2'
self.fields['route'].label_from_instance = self.label_from_instance
#staticmethod
def label_from_instance(obj):
return "pallets: %s, %s, %s, %s" % (obj.pallet_amount, obj.street, obj.city, obj.postal_code)
template:
{% extends 'base.html' %}
{% block base %}
<div class="container rounded bg-white mt-5 mb-5">
<div class="row">
<div class="col-md-5 border-right mx-auto">
planner//route
<div class="p-3 py-5">
<form id="form-container" method="POST">
{% csrf_token %}
{{ form }}
{{ formset }}
<button id="add-form" type="button">Add Another Bird</button>
<button class="btn btn-danger profile-button mt-3" onclick="window.history.back()">Cancel
</button>
<button class="btn btn-primary float-end mt-3" type="submit">Order</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
error:
Cannot assign "<Order: Order object (2)>": "Order.route" must be a "Route" instance.
The error occurs when i fill the formset with request.POST and then try to send the formset to the template.
I have tried an inlineformset and modelformset and didnt get it to work. ANY solution is welcome.

The problem seemed to be with the modelform. I removed this from the code:
class Meta:
model = Order
fields = ("route",)
I don't understand why this caused the problem and if anyone knows and can explain it please feel free to do so.
hope it helps anyone else with this problem.

Related

form.label is not showing in django

this is my code and I try to show the label but it is not displaying
this is my form file
forms.py
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['value', 'body']
labels = {'value': 'Place your vote', 'body': 'Add a comment with your vote'}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})
this is my views file and the function for this form
views.py
def project(request, pk):
projectObj = Project.objects.get(id=pk)
form = ReviewForm()
context = {
'project': projectObj,
'form': form
}
return render(request, 'projects/single-project.html', context)
this is the model for the review section
models.py
class Review(models.Model):
VOTE_TYPE = (
('up', 'Up Vote'),
('down', 'Down Vote')
)
owner = models.ForeignKey(Profile, on_delete = models.CASCADE, null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
body = models.TextField(null=True, blank=True)
value = models.CharField(max_length=200, choices=VOTE_TYPE)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default = uuid.uuid4, unique=True, primary_key=True, editable=False)
class Meta:
# every user can only have single comment on each project
unique_together = [['owner', 'project']]
def __str__(self):
return self.value
and the html file to display the form
html file
<form class="form" action="{% url 'project' project.owner.id %}" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#textarea">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<input class="btn btn--sub btn--lg" type="submit" value="Comments" />
</form>
I try to show form.label but it is not displaying
the label is contained in the label_tag property in the field:
<form class="form" action="{% url 'project' project.owner.id %}" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#textarea">{{field.label_tag}}</label>
{{field}}
</div>
{% endfor %}
<input class="btn btn--sub btn--lg" type="submit" value="Comments" />
</form>
Are you sure for your labels property of Meta class of ModelForm, i do not known this property. You probably need to redefine your field with label you want to, or update field label in the init function of your form.
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['value', 'body']
labels = {'value': 'Place your vote', 'body': 'Add a comment with your vote'}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})

Django Error ''Field 'id' expected a number but got <Transfernews: Transfernews object (1)>.''

I am getting this error when trying to submit comments. Can anyone please tell me how to fix this?
My models.py:
class Transfernews(models.Model):
player_name = models.CharField(max_length=255)
player_image = models.CharField(max_length=2083)
player_description = models.CharField(max_length=3000)
date_posted = models.DateTimeField(default=timezone.now)
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
transfernews = models.ForeignKey(Transfernews, related_name="comments", on_delete=models.CASCADE)
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.transfernews.player_name, self.user.username)
My forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
My views.py:
def transfer_targets(request):
transfernews = Transfernews.objects.all()
news = request.POST.get("transfer_id", None)
form = CommentForm(request.POST or None)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.user = User.objects.get(id=request.user.id)
new_comment.transfernews_id = Transfernews.objects.get(id=news)
new_comment.save()
return redirect(request.path_info)
return render(request, 'transfernews.html', {'transfernews': transfernews, 'form': form})
My html file:
{% for transfer in transfernews %}
<div>Comment and let us know your thoughts</div>
<form method="POST">
{% csrf_token %}
<input type="hidden" name="transfer_id" value="{{ transfer.id}}">
<div class="bg-alert p-2">
<div class="d-flex flex-row align-items-start">
</div>
<div class="mt-2 text-right">
{{ form|crispy }}
<br>
<button class="btn btn-primary btn-sm shadow-none" type="submit">Post comment</button>
<button class="btn btn-outline-primary btn-sm ml-1 shadow-none" type="button">Cancel</button>
</div>
</div>
</form>
{% endfor %}
In your view you should work with:
new_comment.transfernews_id = news
or:
new_comment.transfernews = Transfernews.objects.get(id=news)
but the latter is less efficient since you make an extra database query.
You thus can rewrite the view to:
from django.contrib.auth.decorators import login_required
#login_required
def transfer_targets(request):
transfernews = Transfernews.objects.all()
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
news = request.POST['transfer_id']
form.instance.user = request.user
form.instance.transfernews_id = news
form.save()
return redirect(request.path_info)
else:
form = CommentForm()
return render(request, 'transfernews.html', {'transfernews': transfernews, 'form': form})
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

Django forms how to display related data in an inner form

I am struggling with Django forms.
I have the following model.py:
class Property(models.Model):
portfolio = models.ForeignKey("portfolios.Portfolio", on_delete=models.CASCADE)
class PropertyImage(models.Model):
property = models.ForeignKey("Property", on_delete=models.CASCADE)
image = models.ImageField(upload_to = property_image_upload_to)
def __str__(self):
return self.image.url
class PropertyDocument(models.Model):
property = models.ForeignKey("Property", on_delete=models.CASCADE)
document = models.FileField()
class Address(models.Model):
property = models.OneToOneField("Property", on_delete=models.CASCADE)
line1 = models.CharField(max_length=100)
line2 = models.CharField(max_length=100, null=True, blank=True)
line3 = models.CharField(max_length=100, null=True, blank=True)
post_code = models.CharField(max_length=7)
town = models.CharField(max_length=100, null=True, blank=True)
city = models.CharField(max_length=100)
When adding/updating a property, I want the form to show the form for related objects like the address, documents/images instead of the select list's that appear in forms - I want to be able to add/edit the related data.
My view.py file
class PropertyCreate(CreateView):
model = Property
form_class=PropertyAddressFormSet
success_url = reverse_lazy('Property_list')
def get_context_data(self, **kwargs):
data = super(PropertyCreate, self).get_context_data(**kwargs)
return data
Property_form.html
{% extends 'base/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" class="btn btn-primary" />
<button class="btn btn-link" onclick="javascript:history.back();">Cancel</button>
</form>
{% endblock %}
urls.py
from . import views
app_name = 'properties'
urlpatterns = [
path('<int:portfolio_id>/<int:pk>/edit', views.PropertyUpdate.as_view(), name='property_edit'),
path('<int:portfolio_id>/create', views.PropertyCreate.as_view(), name='property_new'),
]
I've read about inlineformset_factories and inlineformset's etc, but is this the best choice for my scenario? If so, I can't figure out how to show the portfolio, address form
I;m currently using a inlineformset like so, which creates the Address form on the PropertyCreate view, but I want to also add in the PropertyImages and PropertyDocs to the ProertyCreate view.:
PropertyAddressFormSet = inlineformset_factory(
parent_model=Property,
model=Address,
form=AddressForm,
extra=0,
min_num=1
)
For anyone in the same boat as me, I managed to get this working with the following code:
Forms.py:
class PropertyForm(ModelForm):
""" Edit a property """
class Meta:
model = Property
exclude = ()
PropertyAddressFormSet = inlineformset_factory(
parent_model=Property,
model=Address,
form=AddressForm,
extra=0,
min_num=1
)
Views.py
class PropertyCreate(CreateView):
model = Property
form_class=PropertyForm
success_url = reverse_lazy('Property_list')
def get_context_data(self, **kwargs):
data = super(PropertyCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['address'] = PropertyAddressFormSet (self.request.POST, instance=self.object)
else:
data['address'] = PropertyAddressFormSet ()
return data
template:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form |crispy }}
<fieldset class="border p-2">
<legend class="w-auto">Address</legend>
{{ address.management_form }}
{% for form in address.forms %}
<div >
{{ form.as_p }}
</div>
{% endfor %}
</fieldset>
</form>
Hope this helps someone.

Modelset Factory displays a choice fields instead of an input field

Models.py
class Post(models.Model):
title = models.CharField(max_length=100)
conent = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to="post_pics")
def __str__(self):
return self.title
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
class Tag(models.Model):
name = models.CharField(max_length=30)
members = models.ManyToManyField(Post, through='PostTag')
def __str__(self):
return self.name
class PostTag(models.Model):
article = models.ForeignKey(Post, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
Forms.py
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'conent', 'image']
class TagUpdateForm(forms.ModelForm):
class Meta:
model = PostTag
fields = ['tag']
TagUpdateFormSet = modelformset_factory(PostTag, form=TagUpdateForm)
Views.py
#login_required
def updateView(request, pk):
template_name = 'project/post_update.html'
heading_message = 'Tags'
if request.method == 'GET':
form1 = PostUpdateForm(instance=Post.objects.filter(id=pk).first())
form2 = TagUpdateFormSet(queryset=PostTag.objects.filter(article=Post.objects.filter(id=pk).first()))
elif request.method == 'POST':
form1 = PostUpdateForm(request.POST, request.FILES, instance=Post.objects.filter(id=pk).first())
form2 = TagUpdateFormSet(request.POST, queryset=PostTag.objects.filter(article=Post.objects.filter(id=pk).first()))
if form1.is_valid():
invalid = True
for formz in form2:
if formz.is_valid() == False:
invalid = False
break
if invalid:
postform = form1.save(commit=False)
postform.author = request.user
postform.save()
for forms in form2:
data = forms.cleaned_data.get('tag')
if data:
returnedTag, created1 = Tag.objects.get_or_create(name = data)
link, created2 = PostTag.objects.get_or_create(article = postform, tag = returnedTag)
return HttpResponseRedirect('/forum/post/%s/' %(postform.id))
return render(request, 'project/post_update.html', {'form': form1, 'form2': form2})
template
{% extends "template/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% load static %}
<div class="container form-section s-yellow section">
<form method="POST" action="" enctype='multipart/form-data'>
{% csrf_token %}
<fieldset class="form-container">
<legend class="form-top"></legend>
<div class="form1">
{{ form|crispy }}
</div>
<div class="form2 form-group">
{{ form2.management_form }}
{% for formz in form2 %}
<div>
<div class="form-row">
<div class="input-group">
{{formz }}
<div class="">
<button type="button" class="add-form-row">+</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</fieldset>
</form>
</div>
{% endblock content %}
I want to let the user be able to edit all tags in a post, however when i display the tags they show up as choice field that lets the user choose from already existing tags and they can't type a new one.
Is there a way to change the choice field to an input field where the original value is the tag originaly entered.
I tried edditing the 'tag' field to a charfield above the meta tag in the form, but then i was getting an input field with the choice field value (eg 4, 6, 10,) instead of the actual text.
I know the saving part doesnt work, but i need to figure out a way to get the fields dispalyed properly
Btw are there any tutorials for a database structure like this? It's hard to find examples where one table links to 2 others.

How input date in my template ListView (request.POST.get) in Django

I have a class jourListView(ListView). I want to input date in my html template and then retrieve the date to make a filter.
My class jourListView(ListView) refers to two linked tables.
I am stuck at level. How to make my code functional?
here are my models, views and template.
class jour(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE, related_name='jour')
date = models.DateField(blank=True, null=True)
commentaire = models.CharField(max_length=500, blank=True, null=True)
class Meta:
managed = False
db_table = 'jour'
unique_together = (('id'),)
verbose_name = 'JOUR'
verbose_name_plural = 'JOUR'
id = models.AutoField(primary_key=True)
def get_absolute_url(self):
return reverse("jour_detail",kwargs={'pk':self.pk})
def __str__(self):
return str(self.date) ##
class activite(models.Model):
jour = models.ForeignKey('blog.jour',on_delete=models.CASCADE, related_name='jour' )
name= models.CharField(max_length=500, blank=True, null=True)
class Meta:
managed = False
db_table = 'activite'
unique_together = (('id'),)
verbose_name = 'ACTIVITÉ'
verbose_name_plural = 'ACTIVITÉ'
id = models.AutoField(primary_key=True)
def __str__(self):
return self.type
def get_absolute_url(self):
return reverse("jour_list")
My view:
class jourListView(ListView):
model = jour
def get_context_data(self, **kwargs):
if request.method == 'POST':
date_insert = request.POST.get('date_ref')
context = super(jourListView, self).get_context_data(**kwargs)
context['count'] = self.get_queryset().count()
return context
def get_queryset(self):
return jour.objects.filter(date__lte=timezone.now()).filter(user=self.request.user).filter(date__in=date_insert)
My html template:
{% if user.is_authenticated %}
<div class="container text-center">
<form class="form-signin" id="login_form" method="post" action="/blog/list/">
{% csrf_token %}
<br>
<input type="date" name="date_ref" class="form-control" placeholder="SAISIE DATE " value="" required autofocus>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">OK</button>
</form>
</div>
<div class="centerstage">
{% for jour in jour_list %}
<div class="post">
<p class='postcontent' ><strong>Date:</strong> {{ jour.date }}</p>
<p class='postcontent' ><strong>Commentaire:</strong> {{ jour.commentaire }}</p>
<a class="btn btn-primary" href="{% url 'jour_edit' pk=jour.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
<h1>Détails activités</h1>
</div>
-----------------
{% endfor %}
</div>
{% endif %}
I changed my view but it does not work.
class jourListView(ListView):
model = jour
template_name = "blog/list.html"
def get_context_data(self, **kwargs):
if request.method == 'POST':
date_insert = request.POST.get('date_ref')
context = super(jourListView, self).get_context_data(**kwargs)
context['count'] = self.get_queryset().count()
return context
def get_queryset(self):
queryset = jour.objects.filter(date__lte=timezone.now()).filter(user=self.request.user)
date_insert = request.POST.get('date_ref')
if date_insert:
queryset = queryset.filter(date=date_insert)
return queryset
In addition I have another error:
NameError at /blog/list/
name 'request' is not defined
Request Method:
GET
Request URL:
http://127.0.0.1:8000/blog/list/
Django Version:
2.0.2
Exception Type:
NameError
Exception Value:
name 'request' is not defined
You need to define date_insert inside the get_queryset method before you use it. For example:
class jourListView(ListView):
model = jour
def get_context_data(self, **kwargs):
if self.request.method == 'POST':
date_insert = self.request.POST.get('date_ref')
context = super(jourListView, self).get_context_data(**kwargs)
context['count'] = self.get_queryset().count()
return context
def get_queryset(self):
queryset = jour.objects.filter(date__lte=timezone.now()).filter(user=self.request.user)
date_insert = self.request.POST.get('date_ref')
if date_insert:
queryset = queryset.filter(date=date_insert)
return queryset
Once that's working, you may want to consider some further improvements
Renaming the model to Jour to match the Django style
Make sure you return a context in get_context_data for GET requests as well as POST requests.
Using LoginRequiredMixin to make sure that only logged in users can access the view
Adding checks to handle the case where date_insert isn't a valid date string.
I found the solution to my problem.
Here is the result:
I was inspired by the example in this link:
Django: Search form in Class Based ListView
My view:
class jourListView(ListView):
model = jour
template_name = "blog/list.html"
def get_context_data(self, **kwargs):
context = super(jourListView, self).get_context_data(**kwargs)
# FILTER BY CURRENT MONTH, USER
filter_ = jour.objects.filter(date__lte=timezone.now()).filter(user_id=self.request.user).order_by('-date')
if self.request.GET.get('date_ref'):
date_insert = self.request.GET.get('date_ref')
filter_ = filter_.filter(date=date_insert)
context['jourListView'] = filter_
return context
My template (blog/list.html):
{% extends 'blog/base.html' %}
{% block content %}
{% if user.is_authenticated %}
<form class="navbar-form navbar-right" action="." method="get">
{{ form.as_p }}
<input id="date_ref" name="date_ref" type="date" placeholder="Localizar..." class="form-control">
<button type="submit" class="btn btn-success form-control"><span class="glyphicon glyphicon-search"></span></button>
</form>
{% if jourListView %}
{% for select_value in jourListView %}
<div class="post">
<p class='postcontent' ><strong>Date:</strong> {{ select_value.date}}</p>
<p class='postcontent' ><strong>User ID:</strong> {{ select_value.user_id}}</p>
<p class='postcontent' ><strong>Status:</strong> {{ select_value.status}}</p>
</div>
-----------------
{% endfor %}
{% endif %}
{% endif %}
{% endblock %}