enter code hereI'm having some problems to solve a problem. I have a template, which allows the user to change some of their account settings. My goal is to initialize the form, with the user's default values, and he can keep or change them (by after submit form). However, until now the page does not render these values. I'm using a class based view, CreateView, for this purpose.
My code is listed below.
Here, is my CreateView.
class DetailUserInfoView(LoginRequiredMixin ,CreateView):
model = CustomUser.CustomUser
template_name = 'users/InfoUser.html'
login_url = settings.LOGOUT_REDIRECT_URL
context_object_name = 'user'
form_class = CustomUserChangeForm
def get_object(self):
self.model = self.request.user
return self.model
def get_initial(self):
initial = super(DetailUserInfoView, self).get_initial()
initial = initial.copy()
initial[config.USERNAME] = self.request.user.username
initial[config.FIRST_NAME] = self.request.user.first_name
initial[config.LAST_NAME] = self.request.user.last_name
return initial
def get_form_kwargs(self):
kwargs = {'initial': self.get_initial()}
return kwargs
def get_context_data(self, **kwargs): #GET OBJECT ACTS AFTER THAN GET_OBJECT --> EXAMPLE OF GET_CONTEXT_DATA, I DIDN'T NEED THIS
context = super(DetailUserInfoView, self).get_context_data(**kwargs)
context['username'] = self.request.user.username
return context
Here the form.
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = CustomUser.CustomUser
fields = ('email', 'password', 'first_name', 'last_name', 'username', 'userType')
And finally an extract of template.
<div id="infoMayOverride">
<form class="getOverridedValues" method="post">
{% csrf_token %}
<div id="usernameData">
<label>{{ form.username.label_tag }}</label> <!--MODEL ON CREATEUSERVIEW IS CUSTOMUSER, AND NOW I NEED TO USE THIS FIELDS AND INHERITED FIELDS FROM USER CLASS-->
<input type="text" id="usernameInput" value="{{ form.username }}">
</div>
<div id="firstNameData">
<label>{{ form.first_name.label_tag }}</label>
<input type="text" id="firstNameInput" value="{{ form.first_name }}">
</div>
<div id="lastNameData">
<label>{{ form.last_name.label_tag }}</label>
<input type="text" id="lastNameInput" value="{{ form.last_name }}">
</div>
<div id="divBtnChangeProfile">
<input type="submit" class="btnChangeProfile" value="Atualizar Profile">
</div>
</form>
</div>
I'd appreciate it if you could help me. I am new to the Django environment, and have tried many approaches, and I have not yet been able to solve this problem.
--------------------------- Update ------------------------------------
Now, i can get initial values. But to view them i need to write on input form: form.username.initial, and with this i can't after submit form to update user values.
Anyone knows how to solve this type of problem??
I finally got this problem solved. I will make my solution available, since it can help other people.
I had to make some changes to the code I provided behind.
Below is the code of view.
class DetailUserInfoView(LoginRequiredMixin, UpdateView):
model = CustomUser.CustomUser
template_name = 'users/InfoUser.html'
login_url = settings.LOGOUT_REDIRECT_URL
context_object_name = 'user'
form_class = CustomUserChangeForm
def get_object(self, queryset=None):
return self.request.user
def get_form_kwargs(self):
kwargs = super(DetailUserInfoView, self).get_form_kwargs()
u = self.request.user
kwargs['username_initial'] = u.username
kwargs['fName_initial'] = u.first_name
kwargs['lName_initial'] = u.last_name
return kwargs
def get_context_data(self, **kwargs): #GET OBJECT ACTS AFTER THAN GET_OBJECT --> EXAMPLE OF GET_CONTEXT_DATA, I DIDN'T NEED THIS
context = super(DetailUserInfoView, self).get_context_data(**kwargs)
form_class = self.get_form_class()
form = self.get_form(form_class)
context['form'] = form
return context
My form (with init function, to set initial values on form, and is called by def get_form_kwargs(self)).
class CustomUserChangeForm(UserChangeForm):
def __init__(self, *args, **kwargs):
username_initial = kwargs.pop('username_initial', None)
fName_initial = kwargs.pop('fName_initial', None)
lName_initial = kwargs.pop('lName_initial', None)
super(CustomUserChangeForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = username_initial
self.fields['first_name'].initial = fName_initial
self.fields['last_name'].initial = lName_initial
class Meta(UserChangeForm.Meta):
model = CustomUser.CustomUser
fields = ('username', 'email', 'first_name', 'last_name')
And finnaly, in template I replace the tag input with {{ form.username }}.
I hope it can help someone who has the same problem.
Related
I am using Crispy Forms with Bootstrap4. Currently, all the input fields are rendered with form-control class. I want to add form-control-lg to the input fields.
I have tried the following but it is not working
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'first_name', 'last_name']
help_texts = {
'username': None,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].help_text = None
self.fields['password2'].help_text = None
self.fields['password2'].label = "Confirm Password"
self.helper = FormHelper()
self.helper.layout = Layout(
Field(
'username', css_class="form-control form-control-lg my-custom-class"
)
)
Template
<div class="content-section">
<form method="POST" action="">
<h3>Sign Up</h3>
<hr>
{% csrf_token %}
{{ form | crispy }}
</br>
<button type="submit" class="btn btn-primary">Sign Up</button>
</form>
</div>
Also, can I modify the class globally for all input fields?
Following the pattern you already have. Let's start with this method:
1.
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'first_name', 'last_name']
help_texts = {
'username': None,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].help_text = None
self.fields['password2'].help_text = None
self.fields['password2'].label = "Confirm Password"
self.helper = FormHelper()
# Attach to helper
self.helper.form_class = 'form-control-lg'
FormHelper has a list of attributes that can be set, that affect mainly form attributes.
Let’s see how to render the form in a template. Supposing we have the form in the template context as example_form, we would render it doing:
{% load crispy_forms_tags %}
{% crispy example_form example_form.helper %}
Notice that the {% crispy %} tags expects two parameters: first the form variable and then the helper. In this case we use the FormHelper attached to the form, but you could also create a FormHelper instance and pass it as a context variable. Most of the time, you will want to use the helper attached. Note that if you name your FormHelper attribute helper you will only need to do:
{% crispy form %}
Inside project settings.py add this line
CRISPY_CLASS_CONVERTERS = {'textinput': "textinput inputtext"}
For example this setting would generate <input class"textinput inputtext" .... The key of the dictionary textinput is the Django’s default class, the value is what you want it to be substituted with, in this case we are keeping textinput.
So from above you could substitute this way:
CRISPY_CLASS_CONVERTERS = {'input-control': "input-control-lg inputtext"}
References and quotes from Django Crispy Form Doc
Edit
Check this answer
I'm using ListView in my Class Based Views and I want to sort the goods by the selected field by the user, but I couldn't find the information I needed
view
class Shop(ListView):
template_name = 'essense/shop.html'
context_object_name = 'items'
paginate_by = 9
allow_empty = False
def get_queryset(self):
return Item.objects.all()
def get_context_data(self, *, object_list=None, **kwargs):
***context***
return context
def get_ordering(self):
ordering = self.request.GET.get('orderby',)
print(ordering)
return ordering
template
<form action="{% url 'shop' %}" method="get" id="sortProduct">
<div class="product-sorting d-flex">
<p>Sort by:</p>
<select type="submit" name="select">
<option type="submit" name="orderby" value="price">Price: $$ - $</option>
<option type="submit" name="orderby" value="-price">Price: $ - $$</option>
</select>
<input type="submit" class="d-none" value="">
</div>
</form>
You should not override the get_queryset, since there is where the ordering logic is happening. You can specify the model with the model attribute [Django-doc]:
class Shop(ListView):
model = Item
template_name = 'essense/shop.html'
context_object_name = 'items'
paginate_by = 9
allow_empty = False
# no get_queryset
def get_ordering(self):
return self.request.GET.get('orderby',)
or if you want to filter the queryset by making a super call:
class Shop(ListView):
model = Item
template_name = 'essense/shop.html'
context_object_name = 'items'
paginate_by = 9
allow_empty = False
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).all()
def get_ordering(self):
return self.request.GET.get('orderby',)
Note that it is not very safe to let a user select an arbitrary order by. Hashed passwords can be retrieved if one for example orders on user__password for example. Usually it is better to retain a list of fields that are allowed to order by.
I have used as many examples online as I could cobble together in an attempt to get my two simple models:
class Technical_Entry(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
ema = models.ForeignKey(EMA, on_delete=models.CASCADE)
system = models.ForeignKey('System', on_delete=models.CASCADE) # are SYSTEMS RELATED TO SUBSYSTEMS OR JUST TWO GROUPS?
sub_system = models.ForeignKey(SubSystem, on_delete=models.CASCADE)
drawing_number = models.CharField(max_length=200)
drawing_title = models.CharField(max_length=255)
engineer = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
date_drawn = models.DateField()
ab = models.BooleanField()
class Technical_Entry_Files(models.Model):
tech_entry = models.ForeignKey(Technical_Entry, on_delete=models.CASCADE)
file = models.FileField(upload_to='techdb/files/')
def __str__(self):
return self.tech_entry.drawing_number
To upload using a formset. While the page 'displays' mostly correctly, it flat out does not create the record in the Technical_Entry_Files model.
Relevant forms.py:
class FileUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FileUploadForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
class Meta:
model = Technical_Entry_Files
fields = ('file',)
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=1)
class Technical_EntryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Technical_EntryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = Technical_Entry
fields = ('category', 'ema', 'system', 'sub_system', 'drawing_number', 'drawing_title', 'engineer', 'vendor', 'date_drawn', 'ab')
widgets = {
'date_drawn':DateInput(attrs={
'class':'datepicker form-control',
'id' : 'datetimepicker2',
'tabindex' : '1',
'placeholder' : 'MM/DD/YYYY hh:mm',
'autocomplete':'off',
}, format='%m/%d/%Y'),
'system' : Select(attrs={'tabindex':'2'}),
}
Relevant views.py:
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
log_entry_class = Technical_EntryForm(Technical_Entry)
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
# entry = context['object']
# context['entry_id'] = entry.id
# theEntry = Technical_Entry.objects.get(pk=entry.id)
# entry_form = Technical_EntryForm(instance=theEntry)
# context['entry_form'] = entry_form
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super().form_valid(form)
class TechEntryCreateView(LoginRequiredMixin, CreateView):
print ("we are here")
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
print(template_name)
success_url = '/techentry_add'
def get_context_data(self, **kwargs):
data = super(TechEntryCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)
else:
data['file_upload'] = TechFileFormSet()
return data
def form_valid(self, form):
context =self.get_context_data()
file_upload = context['file_upload']
with transaction.atomic():
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryCreateView, self).form_valid(form)
and the tech_entry_form.html:
{% extends 'base.html' %}
{% load static %}
{% block page-js %}
<script>
$('.link-formset').formset({
addText: 'add file',
deleteText: 'remove',
});
</script>
{% endblock %}
{% block content %}
<main role="main" class="container">
<div class="starter-template">
<h1>New Tech Entry</h1>
</div>
<h2> Details of Technical Entry </h2>
<div class="row">
<div class="col-sm">
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<h2> Files </h2>
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
<input type="submit" value="Save"/>back to the list
</form>
</div>
</div>
</div>
</main><!-- /.container -->
{% endblock %}
It will save the entry but not the file that was uploaded, and I see no errors either.
There is no techdb/files folder (maybe I have to create that?) but it certainly does not fail anywhere...creates the Technical_Entry record, but not the Technical_Entry_Files record nor is there any thing on disk added.
Additionally and this is really the biggest piece...it only allows me to upload one file even though the tables should allow many files to one tech entry? (Maybe I need to kick some java script).
All the examples I have found either do not use model based forms, class based views or just seemingly overkill. I just need these simple models to let me upload many files to a technical entry. I thought I was close but fear I am not anywhere near getting this to work :/
1. One thing I noticed is your <form> does not have
<form action="" method="post" enctype="multipart/form-data">
Here 1 says:
No characters are encoded. This value is required when you are using forms that have a file upload control
2. another thing seems missing is: request.FILES
data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)
Not sure if the above is how you should incorporate in your view.
The following is from doc: about inlineformset in a function-based view.
if request.method == 'POST':
formset = ArticleFormSet(request.POST, request.FILES)
Another part of the doc:about File Uploads says:
When Django handles a file upload, the file data ends up placed in request.FILES
I'm trying to have user input the data and store into DB and map with the other data.
Model:
class Code(models.Model):
name = models.CharField(max_length=4, default=None, blank=True, unique=True)
Within the Model, there is another class
class Pull(models.Model):
code_pull = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
How to display to call in the Form and View, so that data is pass when user input the data in the input field.
Form
class Code_Form(forms.ModelForm):
class Meta:
model = Code
fields = ('name',)
class Pull_Form(forms.ModelForm):
class Meta:
model = Pull
fields = ('code_pull', 'data1', 'prefix',)
#Inital Value is NULL
def __init__(self, *args, **kwargs):
super(Pull_Form, self).__init__(*args, **kwargs)
self.fields['code_pull'].queryset = CODE.objects.none()
if 'code_pull' in self.data:
c = self.data.get('code_pull')
self.fields['code_pull'].queryset = CODE.objects.filter(name=c)
#print(self.fields['code_pull'].queryset)
I updated the code for the FORM, so that it initial the value from the CODE_form, Still Error, as the code field is empty
Here is the VIEW:
def InputData(request, *args, **kwargs):
form = Pull_Form(request.POST or None)
if request.method == 'POST':
if form.is_valid():
data_add = form.save(commit=False)
data_add.code = form.cleaned_data['code_pull']
data_add.save()
messages.success(request, 'Successfully')
else:
messages.error(request, form.errors)
return render(request, template_name, {'form': form })
ERROR: Not able to add the data as the field for the code is not selected when submitting the form.
ERROR CODE: code - Select a valid choice. That choice is not one of the available choices.
{{ messages }}
<form id="form1" class="post-form" role=form method="POST" action=".">{% csrf_token %}
<input id="code_pull" class="form-control" type="text" maxlength="4" required></input>
<label for="code_pull">Code</label>
<button type="submit" class="btn">Save</button>
</form>
Thank you for the help in advance.
Django forms use the name attribute in HTML controls to capture form data.
<input id="code" name="code" class="form-control" type="text" maxlength="4" required></input>
I only added name="code". this should make it work.
I doing a form of filtering by the foreignkey field with two search conditions: OR and AND and multiple choices.
forms.py
class EmployeeSkillFilter(forms.ModelForm):
skills = forms.ModelMultipleChoiceField(queryset=Technology.objects.all(), )
class Meta:
model = Employee
fields = ['skills']
templates.py
<form method="get" enctype="multipart/form-data">
{% csrf_token %}
<input type="checkbox" name="and" value="AND"> Choice and<br>
<input type="checkbox" name="or" value="OR"> Choice OR<br>
{{ skill_filter }}
<button type="submit" class="btn btn-info" type="button">Go!</button>
</form>
views.py
class AccountList(AuthorizedMixin, ListView):
model = Employee
template_name = 'employee_list.html'
def get_queryset(self, *args, **kwargs):
condition_and = self.request.GET.get('and', None)
condition_or = self.request.GET.get('or', None)
skill = self.request.GET.get('skills', None)
if condition_and and skill:
object_list = self.model.objects.filter(skills__name_id=skill) ??????
if condition_or and tech:
object_list = self.model.objects.filter(
Q(skills__name_id=skill) |
Q(skills__name_id=skill)
) ?????
else:
object_list = self.model.objects.all()
My urls looks like
localhost://....or=OR&skills=5&skills=4&skills=3
The problem is that when I select multiple objects in a form, I don't know how to pass them all to the condition
skills__name_id=skill
Use the __in lookup [Django-doc] lookup, and in case of and logic, count the number of matches:
from django.db.models import Count
class AccountList(AuthorizedMixin, ListView):
model = Employee
template_name = 'employee_list.html'
def get_queryset(self, *args, **kwargs):
condition_and = self.request.GET.get('and', None)
condition_or = self.request.GET.get('and', None)
skills = self.request.GET.getlist('skills')
if condition_and:
object_list = self.model.objects.filter(
skills__in=skills
).annotate(
nskills=Count('skills')
).filter(
nskills=len(skills)
)
elif condition_or:
object_list = self.model.objects.filter(
skills__in=skills
).distinct()
else:
object_list = self.model.objects.all()