models.py is :
class Todo(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,null=True,blank=True)
title=models.CharField(max_length=200)
desc=models.TextField(null=True,blank=True)
complete=models.BooleanField(default=False)
created=models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['created']
views.py is:
class TaskCreate(generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
create.html is:
<body>
go back
{{ form.as_p }}
<form method="post">
{% csrf_token %}
<input type="submit" value="submit">
</form>
</body>
Whenever I submit data from create.html form it doesn't save it to the database and throws this field is required on 'user' field. How do I resolve this?
You probably want to exclude the user field, since it is determined by the logged in user, so:
from django.conf import settings
class Todo(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, editable=False
)
# …
def __str__(self):
return self.title
class Meta:
ordering = ['created']
Then we inject the logged in user in the instance of the form:
from django.contrib.auth.mixins import LoginRequiredMixin
class TaskCreateView(LoginRequiredMixin, generic.CreateView):
model = Todo
fields = '__all__'
template_name = 'create.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.user = request.user
return super().form_valid(form)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to TaskCreateView, instead of TaskCreate.
Related
I created a form for adding products to an e-Commerce site. The form isn't working perfectly.
First issue: I want to store the user automatically by submitting the form. I actually want to store Who did add the product individually.
Second Issues: The image field is not working, the image is not stored in the database.
How can I fix these issues? help me
forms.py:
from django import forms
from .models import Products
from django.forms import ModelForm
class add_product_info(forms.ModelForm):
class Meta:
model = Products
fields = ('product_title','product_image')
model.py:
class Products(models.Model):
user = models.ForeignKey(User, related_name="merchandise_product_related_name", on_delete=models.CASCADE, blank=True, null=True)
product_title = models.CharField(blank=True, null=True, max_length = 250)
product_image = models.ImageField(blank=True, null=True, upload_to = "1_products_img")
views.py:
def add_product(request):
if request.method == "POST":
form = add_product_info(request.POST)
if form.is_valid():
form.save()
messages.success(request,"Successfully product added.")
return redirect("add_product")
form = add_product_info
context = {
"form":form
}
return render(request, "add_product.html", context)
templates:
<form action="" method="POST" class="needs-validation" style="font-size: 13px;" novalidate="" autocomplete="off" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="d-flex align-items-center">
<button type="submit" class="btn btn-outline-dark ms-auto" style="font-size:13px;">Add</button>
</div>
</form>
You need to set the .user of the .instance wrapped in the form to the logged in user (request.user). Furthermore you need to pass both request.POST and request.FILES to the form to handle files.
from django.contrib.auth.decorators import login_required
#login_required
def add_product(request):
if request.method == 'POST':
form = add_product_info(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
messages.success(request, 'Successfully product added.')
return redirect('add_product')
else:
form = add_product_info()
context = {
'form': form
}
return render(request, 'add_product.html', context)
I would also advise not to use null=True nor blank=True, unless a field is really optional. Likely the product_title should not be optional, nor should the user be, since you use CASCADE in case the user is removed.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: Forms in Django are written in PascalCase, not snake_case,
so you might want to rename the model from add_product_info to ProductInfoForm.
Note: normally a Django model is given a singular name, so Product instead of Products.
why are you using the ForeignKey with your user. the first issue i notice is with the class Meta. Pass this as a list not tuple.
class add_product_info(forms.ModelForm):
class Meta:
model = Products
fields = [
'product_title',
'product_image',
]
then try this as well.
class Products(models.Model):
user = models.OneToOneField(User, related_name="merchandise_product_related_name", on_delete=models.CASCADE, blank=True, null=True)
I am trying to allow users to save details of a workout for a specific exercise through submitting a form. My ExerciseDetailView displays the form how I'd like it to:
class ExerciseDetailView(DetailView):
model = Exercise
template_name = 'workouts/types.html'
def get_context_data(self, **kwargs):
context = super(ExerciseDetailView, self).get_context_data(**kwargs)
context['form'] = WorkoutModelForm
return context
But my problem is with saving the inputted data in the database. I have tried making both a FormView and a CreateView but am clearly missing something:
class ExerciseFormView(FormView):
form_class = WorkoutModelForm
success_url = 'workouts:exercise_detail'
def form_valid(self, form):
form.save()
return super(ExerciseFormView, self).form_valid(form)
Here is my referenced WorkoutModelForm:
class WorkoutModelForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['weight', 'reps']
My template:
<form action="{% url 'workouts:workout' exercise.id %}" method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Save</button>
</form>
Urls:
path('exercise/<int:pk>/detail/', ExerciseDetailView.as_view(), name='exercise_detail'),
path('exercise/<int:pk>/detail/', ExerciseFormView.as_view(), name='workout'),
And for context here is my Workout model which contains a get_absolute_url method:
class Workout(models.Model):
weight = models.FloatField(default=0)
reps = models.PositiveIntegerField(default=0)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
exercise = models.ForeignKey(Exercise, on_delete=models.CASCADE, default=None)
def get_absolute_url(self):
return reverse('exercise_detail', args=[str(self.pk)])
I am not receiving any errors, but when I submit the form my url remains the same, as I hoped, however the page just appears blank and the objects are not recorded. Can anybody please help me see what the problem is?
The problem is not your view, the Django logic will never trigger this view, the URLs are perfectly overlapping, so that means that for a URL, it will always trigger the first view (here the ExerciseDetailView), you should make the paths non-overlapping, for example with:
path('exercise/<int:pk>/detail/', ExerciseDetailView.as_view(), name='exercise_detail'),
path('exercise/<int:pk>/workout/', ExerciseFormView.as_view(), name='workout'),
Triggering the logic will however not be sufficient, since it will not link the Workout to the necessary exercise, you can alter the logic to:
from django.urls import reverse
class ExerciseFormView(CreateView):
form_class = WorkoutModelForm
def form_valid(self, form):
form.instance.exercise_id = self.kwargs['pk']
return super().form_valid(form)
def get_success_url(self):
return reverse('workouts:exercise_detail', kwargs={'pk': self.kwargs['pk']})
Need use CreateView
from django.views.generic.edit import CreateView
class ExerciseFormView(CreateView):
form_class = WorkoutModelForm
...
I have a Django library application with several books and authors, here is a /author/create html form used by the admin to create/update details of the author (firstname, lastname, dob, profile picture), referring the MDN Library application.
I have a Generic Class Based View for this purpose:
class AuthorCreate(PermissionRequiredMixin, CreateView):
permission_required = 'is_superuser'
model = Author
fields = '__all__'
success_url = reverse_lazy('author-detail')
class AuthorUpdate(PermissionRequiredMixin, UpdateView):
permission_required = 'is_superuser'
model = Author
fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']
success_url = reverse_lazy('author-detail')
class AuthorDelete(PermissionRequiredMixin, DeleteView):
permission_required = 'is_superuser'
model = Author
success_url = reverse_lazy('authors')
And these are the url patterns:
urlpatterns += [
path('author/create/', views.AuthorCreate.as_view(), name='author_create'), # redirects to author_form.html
path('author/<int:pk>/update/', views.AuthorUpdate.as_view(), name='author_update'), # redirects to author_form.html
path('author/<int:pk>/delete/', views.AuthorDelete.as_view(), name='author_delete'), # redirects to author_confirm_delete.html
]
And this is the author_form.html for creating/updating author details:
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
//remaining code...
</form>
Now, on clicking the submit button in the html form above, it should redirect to author/id page(mentioned in the success_url), however the main concern is that a new author is not getting created in the first place.
I am not sure how the html form data is being saved, whether or not it is being saved in the first place, because the page is redirecting to the success_url.
Code Referred from MDN: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms
In your Author model add,
def get_absolute_url(self):
return reverse('author_detail', kwargs={'pk': self.pk}) #add kwargs and app_name depending upon your requirement.
views.py
class AuthorCreate(PermissionRequiredMixin, CreateView):
permission_required = 'is_superuser'
model = Author
fields = '__all__'
class AuthorUpdate(PermissionRequiredMixin, UpdateView):
permission_required = 'is_superuser'
model = Author
fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']
Other way if you are using form in CreateView and UpdateView,
def form_valid(self, form):
self.object = form.save()
# ....
return HttpResponseRedirect(self.get_success_url())
If you want to go to previous page only, then this SO question has an answer
In my app, I have Users create Post objects. Each Post has a User
class Post(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE)
...
I want to create a post-submission form for editing and submission, so I plan to use Django's ModelForm functionality.
class PostForm(ModelForm):
class Meta:
model = Post
fields = "__all__"
However, if I do this, then whoever is viewing the form will be able to set who the Post author is. I want to make sure that the resulting user field is them. But, if I exclude the user field from the ModelForm,
class PostForm(ModelForm):
class Meta:
model = Post
exclude = 'user'
then the user will not be set on form submission. I've hacked my way around this by making a custom form and updating the post field
def submit_view(request):
....
request.POST = request.POST.copy()
request.POST.update({
'user' : request.user.id
})
form = PostForm(request.POST, request.FILES)
....
but then I lose automatic UI generation and form validation, which in some ways defeats the purpose of the Form class. Could somebody point me to the idiomatic way of setting the user field without including it in the Form?
Try this view:
def submit_view(request):
form = PostForm(request.POST or None)
if form.is_valid():
new_post = form.save(commit=False)
new_post.user = request.user
new_post.save()
view.py
from django.views.generic import CreateView
from .models import Post
class PostCreate(CreateView):
model = Post
template_name ="new_Post_form.html"
fields = ['text']
def form_valid(self, form):
object = form.save(commit=False)
object.user = self.request.user
object.save()
return super(PostCreate, self).form_valid(form)
def get_success_url(self):
return "/"
url.py
url(r'^newpost$',views.PostCreate.as_view(),name='post_new',),
new_post_form.html
<form method="post" enctype="multipart/form-data" class="form" action="newpost" id="new-post-form">
<div class="modal-body">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</div>
I need to display one form, with multiple fields from 2 different models.
Form will contain only part of fields from models, and layout will be made using the crispy forms.
My models:
class Company(BaseModel):
title = models.CharField(_('Company'), max_length=128)
domain = models.CharField(_('Domain'), max_length=128)
class Account(BaseModel):
company = models.ForeignKey(Company)
user = models.OneToOneField(User)
role = models.CharField(_('Role'), choices=ROLES, default='member', max_length=32)
Fields which I want to show in form:
company title, user first name, user last name, user email
Is it even possible? How can I do this?
The other answers on this page involve tossing away the benefits of model forms and possibly needing to duplicate some of the functionality you get for free.
The real key is to remember that one html form != one django form. You can have multiple forms wrapped in a single html form tag.
So you can just create two model forms and render them both in your template. Django will handle working out which POST parameters belong to each unless some field names clash - in which case give each form a unique prefix when you instantiate it.
Forms:
class CompanyForm(forms.ModelForm):
class Meta:
fields = [...]
model = Company
class AccountForm(forms.ModelForm):
class Meta:
fields = [...]
model = Account
View:
if request.method == 'POST':
company_form = CompanyForm(request.POST)
account_form = AccountForm(request.POST)
if company_form.is_valid() and account_form.is_valid():
company_form.save()
account_form.save()
return HttpResponseRedirect('/success')
else:
context = {
'company_form': company_form,
'account_form': account_form,
}
else:
context = {
'company_form': CompanyForm(),
'account_form': AccountForm(),
}
return TemplateResponse(request, 'your_template.html', context)
Template:
<form action="." method="POST">
{% csrf_token %}
{{ company_form.as_p }}
{{ account_form.as_p }}
<button type="submit">
</form>
In your forms.py
from django import forms
class YourForm(forms.Form):
title = forms.CharField()
first_name = forms.CharField()
last_name = ...
In your views.py
from forms import YourForm
from django import views
from models import Company, Account
class YourFormView(views.FormView)
template_name = 'some_template.html'
form_class = YourForm
success_url = '/thanks/'
def form_valid(self, form):
title = form.cleaned_data['title']
...
# do your processing here using Company and Account
# i.e. company = Company.objects.create(title=title, ...)
# account = Account.objects.get_or_create(
# user=..., company=company ...)
# ... more processing
#
# Call company.save() and account.save() after adding
# your processed details to the relevant instances
# and return a HttpResponseRedirect(self.success_url)
def is_valid(self):
# don't forget to validate your fields if need be here
As usual the docs are pretty helpful.
https://docs.djangoproject.com/en/1.7/topics/forms/