I'm new in Django. I try to build a class based view in order to create an object.
The default name of the form in the template is form and I want to change it to "ajoutersource" but I can't figure how.
views.py
class ajoutSource(CreateView):
model = Source
template_name = "pmd/ajouterSource.html"
form_class = AjouterSourceForm
success_url = reverse_lazy(ListerSources)
ajouterSource.html
{% for field in ajoutersource %}
<div class="row">
{% if field.errors %}
<div class="error">{{ field.errors }}</div>
{% endif %}
<div class="label">{{ field.label }}</div>
<div class="field">{{ field }}</div>
</div>
{% endfor %}
Override get_context_data():
class ajoutSource(CreateView):
model = Source
template_name = "pmd/ajouterSource.html"
form_class = AjouterSourceForm
success_url = reverse_lazy(ListerSources)
def get_context_data(self, **kwargs):
context = super(ajoutSource, self).get_context_data(**kwargs)
context["ajoutersource"]=context["form"]
return context
you can do it simply by following method
Method 1 (Model Form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['new_name'] = self.get_form()
return context
Method 2 (Simple Form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['new_name'] = context["form"]
return context
Method 1 is recommended
(Note: This is python 3.6+ syntax, change super() call for python 2.0+)
Override the get_context_data, context['form'] take from SomeForm, and change to form_1, you can use in template as form_1
class Something(generic.CreateView):
template_name = 'app/example.html'
form_class = forms.SomeForm
model = models.SomeModel
def get_context_data(self, **kwargs):
context = super(Something, self).get_context_data(**kwargs)
context["form_1"] = context["form"]
context["form_2"] = forms.SomeForm2(**self.get_form_kwargs())
return context
Related
I'm trying to make an UpdateView to edit user profile without pk or slug using get_object() with self.request.user. But when I run my settings, the form is not shown. Here is my code:
urls.py:
urlpatterns = [
path('settings/', UserSettings.as_view(), name='settings'),
path('<slug:profile_slug>/', ShowUserProfile.as_view(), name='profile'),
]
views.py:
class UserSettings(LoginRequiredMixin, DataMixin, UpdateView):
template_name = 'users/user_settings.html'
form_class = UpdateUserForm
def get_object(self, *args, **kwargs):
return self.request.user
def get_success_url(self):
return reverse_lazy('profile', kwargs={'profile_slug': self.request.user.userpofile.slug})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
c_def = self.get_user_context(title='Settings')
forms.py:
class UpdateUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'password')
widgets = {'username': forms.TextInput(attrs={'class': 'form-control form-input form__input'}),
'password': forms.TextInput(attrs={'class': 'form-control form-input form__input'})}
user_settings.html:
<form class="form" method="post">
{% csrf_token %}
{% for item in form %}
<div class="row">
<div class="col-sm-4">
<label class="form__label" for="{{item.id_for_label}}">{{item.label}}: </label>
</div>
<div class="col-sm-8">
{{item}}
</div>
</div>
<div class="form__errors">{{item.errors}}</div>
{% endfor %}
<button class="button" type="submit">Set settings</button>
</form>
It doesn't even throw an error. What I need to do?
You need to return you get_context_data function, like so:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
c_def = self.get_user_context(title='Settings')
return context
The view has a Boolean Field which will define if a question is OK or needs correction.
The template will load two buttons to act as submit to the form, "Question is OK" and "Question needs correction".
I need to pass the value of this button as the Boolean Field value.
I found the answer when using Function-based views, but I'm using Class-based views, so I don't know how to pass the request.POST values.
Here's my views.py and forms.py:
views.py
class QuestionValidation(PermissionRequiredMixin, UpdateView):
permission_required = 'users.validator'
model = Question
form_class = ValidationForm
template_name = 'question_validation.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
context['question'] = Question.objects.filter(
question_order=self.kwargs['order']).get(id_by_order=self.kwargs['id_by_order'])
context['order'] = self.kwargs['order']
context['id_by_order'] = self.kwargs['id_by_order']
return context
def get_object(self, *args, **kwargs):
question_order = Q(question_order__id=self.kwargs['order'])
question_id = Q(id_by_order__contains=self.kwargs['id_by_order'])
q = Question.objects.get(question_order & question_id)
return get_object_or_404(Question, pk=q.id)
def get_success_url(self, *args, **kwargs):
view_name = "order-detail"
return reverse(view_name, kwargs={'pk': self.kwargs['order']})
forms.py
class ValidationForm(forms.ModelForm):
class Meta:
model = Question
fields = ['revision_report', 'revision_approval']
widgets = {
'revision_report': forms.HiddenInput(),
'revision_approval': forms.HiddenInput(),
}
and part of the template that this code will be loaded:
<form action="" method="POST">{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-success" name="question_approved">Questão aprovada</button>
<button class="btn btn-danger" name="question_refused">Questão não foi aprovada</button>
</form>
<br><br>
<script src="{% static 'js/hoverValidatorTextbox.js' %}"></script>
{% endblock %}
As Ene P told in the comments the following link solves it.
https://docs.djangoproject.com/en/3.2/topics/class-based-views/intro/#handling-forms-with-class-based-views
I have a form in ListView view using FormMixin that form takes a string lastname
and querys to take a queryset of Account object, I want that Queryset on my template via extra_context.
The form works and I have the queryset that I want, I pass it into self.extra_context and I can see it on console with print BUT it doesn't show in my template. Im feeling stacked, please help me if tou can.
class DepartmentFilesListView(LoginRequiredMixin, SelectRelatedMixin,FormMixin, ListView):
model = FileDetail
login_url = '/'
template_name = 'incomingfiles/departmentFileList_All.html'
context_object_name = 'departmentFiles'
form_class = ChooseOperatorForm
success_url = reverse_lazy('incomingfiles:DepartmentFilesListView')
def __init__(self):
super(DepartmentFilesListView, self).__init__()
now = timezone.now()
self.extra_context = {'title':'Έγγραφα Τμήματος','date':now.date(), 'day':getDay(now.weekday())}
def dispatch(self, request, *args, **kwargs):
return super(DepartmentFilesListView, self).dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# for
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
self.extra_context['operators'] = Account.objects.filter(
is_active=True,
department=self.request.user.department,
last_name__contains=lastname
)
print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_valid(form)
def form_invalid(self, form):
return super(DepartmentFilesListView, self).form_invalid(form)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['filter_for_all'] = FileDetailFilter(self.request.GET, queryset=self.get_queryset())
num_in_page = 50
paginated_filter_for_all = Paginator(context['filter_for_all'].qs, num_in_page)
filter_for_all_page_number = self.request.GET.get('page')
filter_for_all_page_obj = paginated_filter_for_all.get_page(filter_for_all_page_number)
context['filter_context_obj'] = filter_for_all_page_obj
return context
def get_queryset(self):
self.department = self.request.user.department
return FileDetail.objects.filter(Q(Q(apantitiko__operator__department=self.department)
| Q(departmentassignment__department=self.department)))
And this is the sample of my template
<div class="col-12 Search-operator-div">
<div class="row">
<h2>Αναζήτηση χρεωμένων εγγράφων κατα χειριστή</h2>
</div>
<div class="row" style="width:100%; border-top: 2px solid #212529;"></div>
<br>
<form id="exactΟperator" method="post">
{% csrf_token %}
<div class="row">
{% for field in form %}
<div class="col-1 align-self-center">
<label id="id_{{ field.name }}_label">{{ field.label }}</label>
</div>
<div class="col-3 align-self-center">
{{ field }}
{{ field.error }}
<a role="button" id="cancelOperator" type="button">
<i class="fas fa-times"></i></a>
</div>
{% endfor %}
<div class="col-1 align-self-center">
<input id="SearchOperator" type="submit" value="Αναζήτηση" class="btn btn-primary btn-sm">
</div>
</div>
</form>
<div class="row search-operator-div-content">
{% for operator in operators %}
<div class="col">
<a href="{% url 'incomingfiles:OperatorFilesListView' pk=operator.pk %}" class="link-transition btn-link">
{{ operator }}</a>
</div>
{% endfor %}
</div>
<br>
</div>
In my template I can see anything else is in my self.extra_context but anything Im tried to pass in my form_valid function doesn't shown.
It is like my extra_context cant update in this function.
The default implementation for form_valid() simply redirects to the success_url.
So it returns:
return HttpResponseRedirect(self.get_success_url())
And calling extra_context_data is skipped.
SOLUTION
Return HttpResponse instead super(DepartmentFilesListView, self).form_valid(form):
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
self.extra_context['operators'] = Account.objects.filter(
is_active=True,
department=self.request.user.department,
last_name__contains=lastname
)
print(self.extra_context['operators'])
return render(self.request, self.template_name, self.get_context_data())
I think this is the easiest method:
class SomeListView(ListView):
.....
extra_context = self.myquery()
.....
def myquery(self):
return { 'query': QuerySet}
Then in template
{{query}}
Well I found a solution but I don't know if that's the correct way to solve that problem.
If you have anything to suggest it will be my pleasure to try it.
To solve my problem I pass the lastname value into the session and query in get_context_data .
def form_valid(self, form):
lastname = form.cleaned_data['lastname']
lastname = valuateLastname(lastname)
if lastname == '':
lastname = '-'
elif lastname == '*':
lastname = ''
self.request.session['lastname'] = lastname
# print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_valid(form)
def form_invalid(self, form):
print(self.extra_context['operators'])
return super(DepartmentFilesListView, self).form_invalid(form)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['filter_for_all'] = FileDetailFilter(self.request.GET, queryset=self.get_queryset())
num_in_page = 50
paginated_filter_for_all = Paginator(context['filter_for_all'].qs, num_in_page)
filter_for_all_page_number = self.request.GET.get('page')
filter_for_all_page_obj = paginated_filter_for_all.get_page(filter_for_all_page_number)
context['operators'] = Account.objects.filter(is_active=True,
department=self.request.user.department,
last_name__contains=self.request.session['lastname'])
context['filter_context_obj'] = filter_for_all_page_obj
return context
I have a CBV that use ListView at first here my views.py
class InventoryListView(ListView):
context_object_name = 'inventorys'
model = models.Inventory
and here my template_list.html
{% for inventory in inventorys %}
<tr>
<td>{{ inventory.name }}</td>
<td>{{ inventory.sn }}</td>
<td>{{ inventory.employee.name }}</td>
<td>{{ inventory.desc }}</td>
</tr>
{% endfor %}
it returns all the data as expected.
but I need add form with it. and then add some of code to my views.py
class InventoryListView(ListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
def get(self, request):
form = InventoryForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = InventoryForm(request.POST)
and here my forms.py
class InventoryForm(forms.ModelForm):
name = forms.CharField(max_length=255)
sn = forms.DecimalField(max_digits=20, decimal_places=0)
desc = forms.CharField(widget=forms.Textarea)
employee = forms.ModelChoiceField(queryset=Employee.objects.all(), to_field_name="id")
class Meta:
model = Inventory
fields = ('name', 'sn', 'desc', 'employee')
and here my template_list.html
{% for inventory in inventorys %}
<tr>
<td>{{ inventory.name }}</td>
<td>{{ inventory.sn }}</td>
<td>{{ inventory.employee.name }}</td>
<td>{{ inventory.desc }}</td>
</tr>
{% endfor %}
<form method="post" action="{% url 'system:inventory_create' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
now form working perfectly and checked on my DB its submitted. but list of data not showing like before, because I add:
def get(self, request):
form = InventoryForm()
return render(request, self.template_name, {'form': form})
to my views.py
so how to make both works, list of data & form.
Try to avoid overriding get or post for generic class-based-views. It's easy to end up duplicating existing functionality or having to repeat code.
In this case, you can add the form to the template context by overriding the get_context_data method.
class InventoryListView(ListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
def get_context_data(self, **kwargs):
context = super(InventoryListView, self).get_context_data(**kwargs)
context['form'] = InventoryForm()
return context
...
Send the form through get_context_data() method:
def get_context_data(self, **kwargs):
context = super(InventoryListView,self).get_context_data(**kwargs)
context['form'] = InventoryForm()
return context
I have a similar situation. I tried many things, and now using ListView with FormMixin.
First, I make FormListView inheriting ListView and FormMixin. Here's my code
from django.http import Http404
from django.views.generic import ListView
from django.views.generic.edit import FormMixin
from django.utils.translation import ugettext as _
class FormListView(FormMixin, ListView):
def get(self, request, *args, **kwargs):
# From FormMixin
form_class = self.get_form_class()
self.form = self.get_form(form_class)
# From ListView
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty and len(self.object_list) == 0:
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
% {'class_name': self.__class__.__name__})
context = self.get_context_data(object_list=self.object_list, form=self.form)
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
As you see, I made my own get for FormMixin andListView` both.
And inheriting this, I make my own SomeFormListView, In your case InventoryFormListView.
class InventoryFormListView(FormListView):
template_name ='system/inventory_list.html'
context_object_name = 'inventorys'
model = models.Inventory
# FormListView
form_class = YourCustomModelForm
# your custom method
To save the state of the form
def get_context_data(self, **kwargs):
context = super(RecentChannels, self).get_context_data(**kwargs)
context['form'] = RecentChannelsForm(self.request.GET)
return context
I only used this in my template. I am following a tutorial. In tutorial video is working but I couldn't make it work. Here is my template code:
<i> {% for object in object_list %}
<div class="media">
<div class="media-left">
<a href="#">
{% if object.img %} <img class="media-object" src="..." alt="...">
{% endif %}
</a>
</div>
<div class="media-body">
{{ object.content }}<br/>
via {{ object.user }} | {{ object.timestamp|timesince }} ago |
<a href='{{ object.get_absolute_url }}'>View</a>
</div>
</div>
<hr/> </i>
My views.py
<i>
Create your views here.
class TweetCreateView(FormUserNeededMixin, CreateView):
form_class = TweetModelForm
template_name = 'tweets/create_view.html'
class TweetUpdateView(LoginRequiredMixin, UserOwnerMixin, UpdateView):
form_class = TweetModelForm
queryset = Tweet.objects.all()
template_name = 'tweets/update_view.html'
success_url = '/tweets/'
class TweetDeleteView(LoginRequiredMixin, DeleteView):
model = Tweet
template_name = 'tweets/delete_confirm.html'
success_url = reverse_lazy('tweets:home')
class TweetDetailView(DetailView):
template_name = "tweets/detail_view.html"
queryset = Tweet.objects.all()
def get_object(self):
print(self.kwargs)
pk = self.kwargs.get("pk")
print(pk)
return Tweet.objects.get(id=pk)
class TweetListView(ListView):
template_name = "tweets/tweets_list.html"
def get_queryset(self, *args, **kwargs):
qs = Tweet.objects.all()
print(self.request.GET)
query = self.request.GET.get("q", None)
if query is not None:
qs = qs.filter(Q(content=query))
return qs
def get_context_data(self, *args, **kwargs):
context = super(TweetListView, self).get_context_data(*args, **kwargs)
context['create_form'] = TweetModelForm()
context['create_url'] = reverse_lazy('tweets:create')
return context
</i>
my models.py
<i>
class Tweet(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, )
content = models.CharField(max_length=140, validators=[ validate_content ])
updated = models.DateField(auto_now=True, )
timestamp = models.DateField(auto_now_add=True, )
def __str__(self):
return str(self.content)
</i>
You have to add methode get_absolute_url to your models, it's should look like that :
def get_absolute_url(self):
from django.urls import reverse
return reverse('tweets:detail', kwargs={
'pk': self.pk
})