In documentation of Django Wizard i found code like this:
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
So I am wondering how can i add multiple forms to single step of wizard
Make one of your forms a Formset containing the rest of the forms you need. You don't need to necessarily use a ModelFormset, you can subclass the base class and create the forms manually.
This is now deprecated use this link: https://github.com/vikingco/django-formtools-addons
I wanted to share my settings if would be any help to anyone:
class BaseImageFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseImageFormSet, self).__init__(*args, **kwargs)
self.queryset = Images.objects.none()
ImageFormSets = modelformset_factory(Images, formset=BaseImageFormSet, fields=('picture',), extra=2)
form_list = [("step1", CategoryForm),
("step2", CityForm),
("step3", (
('lastform', LastForm),
('imageform', ImageFormSets)
))
]
templates = {"step1": "create_post_category.html",
"step2": "create_post_city.html",
"step3": "create_post_final.html"}
class OrderWizard(SessionMultipleFormWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'photos'))
def get_template_names(self):
return [templates[self.steps.current]]
def render(self, forms=None, **kwargs):
forms = forms or self.get_forms()
context = self.get_context_data(forms=forms, **kwargs)
#print(forms[1](queryset = Images.objects.none()))
return self.render_to_response(context)
def done(self, form_list, form_dict, **kwargs):
form_data_dict = self.get_all_cleaned_data()
#print(form_data_dict)
result = {}
instance = Post()
#print(form_dict)
for key in form_dict:
form_collection = form_dict[key]
#print(form_collection)
for key in form_collection:
form = form_collection[key]
print('printing form %s' % key)
#if isinstance(form, forms.ModelForm):
if key == 'lastform':
post_instance = form.save(commit=False)
nodes = form_data_dict.pop('nodes')
city = form_data_dict.pop('city')
post_instance.save()
post_instance.category.add(nodes)
post_instance.location.add(city)
print('lastfome as esu ')
if key == 'imageform':
for i in form_data_dict['formset-step3']:
picture = i.pop('picture')
images_instance = Images(post=post_instance, picture=picture)
images_instance.save()
return render_to_response('create_post_done.html', {
'form_data': result,
#'form_list': [form.cleaned_data for form in form_list],
})
I've implemented an extension to the Django Wizard, which supports multiple Forms in one wizard step:
https://pypi.python.org/pypi/django-multipleformwizard
Related
I have a problem when I want to paginate the filter that I create with django_filter, in my template it shows me the query set and filter but paginate does not work, I would like to know why this happens and if you could help me.
I'll insert snippets of my code so you can see.
This is my views.py
PD: i have all the necesary imports.
#method_decorator(staff_member_required, name='dispatch')
class EmployeeListView(ListView):
model = Employee
paginate_by = 4
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('employee.view_employee'):
return redirect(reverse_lazy('home'))
return super(EmployeeListView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = EmployeeFilter(self.request.GET, queryset = self.get_queryset())
return context
filters.py
import django_filters
from .models import Employee, Accident
class EmployeeFilter(django_filters.FilterSet):
class Meta:
model = Employee
fields = {
'rutEmployee' : ['startswith']
}
You should override get_queryset.This means you have to put your filter in get_queryset like this:
#method_decorator(staff_member_required, name='dispatch')
class EmployeeListView(ListView):
model = Employee
paginate_by = 4
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('employee.view_employee'):
return redirect(reverse_lazy('home'))
return super(EmployeeListView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = EmployeeFilter(self.request.GET, queryset = self.get_queryset())
return context
def get_queryset(self):
queryset = super().get_queryset()
return EmployeeFilter(self.request.GET, queryset=queryset).qs
and use object_list instead of filter in employee_list.html like this:
{% for employee in object_list|dictsort:"id" reversed %}
You could also try this:
(A snippet from my source code)
class ModelListView(ListView):
model = YourModel
paginate_by = 4 # Change this if you don't intend to paginate by 4
ordering = model_field_to_order_by
# variable used to know if a match was found for the search made using django_filters
no_search_result = False
def get_queryset(self, **kwargs):
search_results = YourDjangoFiltersForm(self.request.GET, self.queryset)
self.no_search_result = True if not search_results.qs else False
# Returns the default queryset if an empty queryset is returned by the django_filters
# You could as well return just the search result's queryset if you want to
return search_results.qs.distinct() or self.model.objects.all()
def get_query_string(self):
query_string = self.request.META.get("QUERY_STRING", "")
# Get all queries excluding pages from the request's meta
validated_query_string = "&".join([x for x in re.findall(
r"(\w*=\w{1,})", query_string) if not "page=" in x])
# Avoid passing the query path to template if no search result is found using the previous query
return "&" + validated_query_string.lower() if (validated_query_string and not self.no_search_result) else ""
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Pass to template if you want to do something whenever an empty queryset is return by django_filters
context["no_search_result"] = self.no_search_result
# This is the query string which should be appended to the current page in your template for pagination, very critical
context["query_string"] = self.get_query_string()
context['filter'] = YourDjangoFiltersForm()
return context
In your html template you need to append the querystring passed to your template from the view, a sample is shown below
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active" aria-current="page">
<span class="page-link">{{ i }}<span class="sr-only">(current)</span></span>
</li>
{% elif i > page_obj.number|add:'-5' and i < page_obj.number|add:'5' %} <li class="page-item"><a
class="page-link" href="?page={{ i }}{{ query_string }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
I am currently following Mozilla's Django tutorial (https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms). The tutorial mostly shows how to create form using functions. I am trying to make the same function view work by using a generic class view (FormView). I am able to make most of the code to work except for 2 things. First one is that I can't seem to be able to save the due date. And, second one, is that I don't know how to access the model fields in my template using template variables.
Here is my form model from the forms.py file.
class RenewBookModelForm(ModelForm):
def clean_due_back(self):
data = self.cleaned_data['due_back']
# Check if a date is not in the past.
if data < datetime.date.today():
raise ValidationError(ugettext_lazy(
'Invalid date - renewal in past'))
# Check if a date is in the allowed range (+4 weeks from today).
if data > datetime.date.today() + datetime.timedelta(weeks=4):
raise ValidationError(ugettext_lazy(
'Invalid date - renewal more than 4 weeks ahead'))
# Remember to always return the cleaned data.
return data
class Meta:
model = BookInstance
fields = ['due_back']
labels = {'due_back': ugettext_lazy('New renewal date')}
help_texts = {'due_back': ugettext_lazy(
'Enter a date between now and 4 weeks (default 3).')}
The form model implemented as a function:
#permission_required('catalog.can_mark_returned')
def renew_book_lirarian(request, pk):
book_instance = get_object_or_404(BookInstance, pk=pk)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = RenewBookModelForm(request.POST)
# Chech if the form is valid
if form.is_valid():
# process that data in form.cleaned_data as required (here we just write to the model due_back field)
book_instance.due_back = form.cleaned_data['due_back']
book_instance.save()
# redirect to a new URL
return HttpResponseRedirect(reverse('all-borrowed'))
# If this is a GET (or any other method) create the default form.
else:
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
form = RenewBookModelForm(initial={'due_back': proposed_renewal_date})
context = {
'form': form,
'book_instance': book_instance
}
return render(request, 'catalog/book_renew_librarian.html', context=context)
This is my class-based view from my views.py file:
class RenewBookLibrarian(LoginRequiredMixin, PermissionRequiredMixin, generic.FormView):
"""Generic class-based view for forms."""
template_name = 'catalog/book_renew_librarian.html'
permission_required = 'catalog.can_mark_returned'
form_class = RenewBookModelForm
success_url = reverse_lazy('all-borrowed')
def get_initial(self):
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
initial = {'due_back': proposed_renewal_date}
return initial
And finally this is my template file where I wish to access the model fields:
{% extends 'base_generic.html' %}
{% block content %}
<h1>Renew: {{ book_instance.book.title }}</h1>
<p>Borrower: {{ book_instance.borrower }}</p>
<p {% if book_instance.is_overdue %} class="text-danger" {% endif %}>Due date: {{ book_instance.due_back }}</p>
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="submit">
</form>
{% endblock %}
The book_instance variable in the template is not working, hence I would like to know how I can display fields from my BookInstance model.
To add book_instance to the template context, you can override get_context_data.
In the FormView, instead of checking if form.is_valid(), you override the form_valid method (see the class based view docs for basic forms).
class RenewBookLibrarian(LoginRequiredMixin, PermissionRequiredMixin, generic.FormView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['book_instance'] = get_object_or_404(BookInstance, pk=self.kwargs['pk'])
def form_valid(self, form):
book_instance = get_object_or_404(BookInstance, pk=self.kwargs['pk'])
book_instance.due_date = form.cleaned_data['due_date']
book_instance.save()
return super().form_valid(form) # will redirect to the success url
I can't understand why nothing comes out in the html template.
class TrainersListView(ListView):
model = Profile
template_name = 'trainers.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
_list = Profile.objects.filter(city__slug=self.kwargs['slug']).order_by('id')
context['trainers'] = _list
print(len(context['trainers']) --> return 5
html
{% for trainer in trainers %}
{{ trainer.id }}
{% endfor %}
Even if I take out all the instances
_list = Profile.objects.all()
still a blank result
Am I doing everything right?
You forgot
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
_list = Profile.objects.filter(city__slug=self.kwargs['slug']).order_by('id')
context['trainers'] = _list
print(len(context['trainers']) --> return 5
return context
Doc: Generic display views
I couldn't find a similar question (perhaps because I do not know the correct verbiage), but this is my dilemma. I have two models, one showing a relationship of the other.
I want to be able to compare the 'user_list' and 'friends_list' in the html template so that I can determine whether to not show a user in the "available users" section if a friend relationship already exists.
However, the 'user_list' objects are of the CustomUser model type, and the 'friends_list' objects are of the Friend model type. How can I return the results of the get_friends() method such that I can exclude these friends from the 'user_list' queryset, preferably by pk so I don't have to worry about string comparisons?
Thank you!
Models.py
class CustomUserModel(AbstractBaseUser, PermissionsMixin):
# Simplified for brevity
username = models.CharFiend(_('username'), unique=True)
class Friend(models.Model):
user1 = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete.models.CASCADE, related_name='user1')
user2 = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete.models.CASCADE, related_name='user2')
objects = FriendManager()
class Meta:
unique_together = ('user1', 'user2')
def __str__(self):
return str(self.user2)
Managers.py
class FriendManager(models.Manager):
def is_friend(self, user1, user2):
if not user1 or not user2:
return False
return super(FriendManager, self).get_queryset().filter(user1=user1).filter(user2=user2).exists()
def get_friends(self, user):
return super(FriendManager, self).get_queryset().filter(user1=user)
Views.py
class UserList(ListView):
model = get_user_model()
context_object_name = "object_list"
template_name = "users.html"
def dispatch(self, request, *args, **kwargs):
self.user = get_user(request)
return super(UserList, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
return get_user_model().objects.order_by("-date_joined")
def get_friends(self):
if self.user:
return Friend.objects.get_friends(self.user).order_by("user2")
return None
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {
'user_list': self.get_queryset(),
'friends_list': self.get_friends(),
'user': self.user
})
Users.html
{% extends 'base.html' %}
{% block content %}
<h1>Available Users</h1>
{% for user in user_list %}
{% if user not in friends_list %} <!-- This is my pain-point -->
<p>{{ user }}</p>
{% endif %}
{% endfor %}
<h1>Friends</h1>
{% for friend in friends_list %}
<p>{{ friend }}</p>
{% endfor %}
{% endblock content %}
You can simply change your query to get CustomUser. So the query should be something like CustomeUser.objects.filter(user2__user1=self.user) this will give you the CustomUsers that are friends with self.user. Depending on how you interpret the relation you should also need to query for CustomUser.objects.filter(user1__user2=self.user). These queries should replace your query in the get_friends() method of your view.
so I want to create a dynamic number of forms depending on a variable.
Here is my forms.py
class CTestform(forms.Form):
def __init__(self, c_test_tokens, *args, **kwargs):
super(CTestform, self).__init__(*args, **kwargs)
wordsbeforegap = ''
iteratorforgaps = 0
for i in (0, len(c_test_tokens)-1):
if '#GAP#' not in c_test_tokens[i]:
wordsbeforegap = wordsbeforegap + c_test_tokens[i]
else:
self.fields[iteratorforgaps] = forms.CharField(widget=forms.TextInput(attrs={'size': '5'}),
required=False, label=wordsbeforegap, label_suffix='')
wordsbeforegap = ''
iteratorforgaps += 1
Here is my views.py where I call CTestform to render:
def ctest(request):
# this function is not really important for the question
# c_test_tokens = list of chars
c_test_tokens, gaps, tokenindexe = generate_c_test(exampletext())
form = CTestform(c_test_tokens=c_test_tokens)
return render(request, 'ctest.html', {'form': form})
I thought that the forms created would be in self.fields, so to print the forms at my website I have in my template this:
<div class="ctest">
{% for forms in form.fields %}
{{ forms }}
{% endfor %}
</div>
But the site is empty and no forms are getting rendered. What could be the problem?
Solved:
Problem was in my for loop
Missed "range" lol
for i in range (0, len(c_test_tokens)-1):