Django Class Based Views : Override form name - python

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

The form for UpdateView for editing the user profile is not displayed. Django

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

Associate a Django Bool FormField with a submit button in a ModelForm and CBV

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

python - django extra_context wont show

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

django ListView with a form

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

get_absolute_url not making any effect

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
})

Categories