ModelForms on an extended User table (inline formset) - python

I extended the Django user in order to have additional fields. I named this entity Persons.
I am using the ModelForm in order to generate my fields. I am making a basic form insertion. When I generate the form, since I am expanding the User model and there exists a one-to-one relationship, the username name shows up as a select box, so users can select from already existing users. However, what I want to do is when a user fills in the form, for a username to be created.
Do I need to create two separate forms with the same submit, or its possible to use an inline formset? What would be the best way to solve this? I am declaring my form like this:
def trainer_signup(request):
TrainerFormSet = modelformset_factory(Trainer)
if request.method == 'POST':
formset = TrainerFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
# do something.
else:
formset = TrainerFormSet()
return render_to_response("forms.html", {
"formset": formset,
})

Related

Taking action after registering new user in Django

I want take some action right after creating a new User using UserCreationForm.
In this situation i need to create a new instance of Model which is in one-to-one relationship with this new User.
Please tell me how can i connect my code which will create this Model instance to the UserCreations form or to the register view.
I know that there is a save method in UserCreationForm which finally creates new User, but i do not know how to connect it to my code
this is how my view looks like
def signup(request):
if request.method == "POST":
form = MyUserCreationForm(request.POST)
if form.is_valid():
form.save()
return redirect('login')
else:
form = MyUserCreationForm()
return render(request, 'registration/signup.html', {"form": form})
You can return the newly created user instance like this new_user=form.save() and then you can create another model based on this user
if form.is_valid():
new_user = form.save()
YourModel.objects.create(user=new_user,....
return redirect('login')
i think signals could help you.
you can write a signal, to do something after a instance of model creates.
https://docs.djangoproject.com/en/3.0/topics/signals
https://docs.djangoproject.com/en/3.0/ref/signals

Dynamically Add Fields to Django Models & Migrate & Return on Template

I want superuser or agent to be able to add fields to user profile on the go during collection of data from users.
For example, the agent will click add new field button via frontend, and the link will automatically create the field, add to the 'Profile' model and makemigrations via django South. Now I tried doing this via inlineformset factory adding additional fields.
The problem is the fields are not being created in the database so saving data inputted in the fields are not working too.
I have 'Profile'model for user details and 'Userform' for details related to User model.
So now below is my forms and views
class BaseProfileEditInlineFormSet(BaseInlineFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
form.fields['more_field'] = forms.CharField()
form.fields['more_field'] = forms.CharField()
views
#login_required
def edit_user_profile(request, pk):
userpro = CustomUser.objects.get(pk=pk)
user_form = UserForm(instance=userpro)
ProfileInlineFormset = inlineformset_factory(CustomUser, Profile, exclude=['user','pub_date','modified_date','basic_verification','phone_confirmed'],formset=BaseProfileEditInlineFormSet, max_num=1, can_delete=False)
formset = ProfileInlineFormset(instance=userpro)
if request.user.is_authenticated and request.user.id == userpro.id:
if request.method == "POST":
user_form = UserForm(request.POST, request.FILES, instance=userpro)
formset = ProfileInlineFormset(request.POST, request.FILES, instance=userpro)
if user_form.is_valid():
created_user = user_form.save(commit=False)
formset = ProfileInlineFormset(request.POST, request.FILES, instance=userpro)
if formset.is_valid():
created_user.save()
formset.save()
return HttpResponse('saved data')
return render(request, 'accounts/account_update.html', {'pk':pk, 'user_form': user_form, 'formset':formset,})
else:
raise PermissionDenied
What am I missing?
Also do you think this is a nice idea based on the fact that migrations in production will be different from the one in local server? Or it's better to define the fields in models instead of adding this feature. I need enlightenment.

Django Admin - Custom Inline Form

I'm attempting to use a custom inline form within the django admin.
admin.py --
class EmpInline(admin.StackedInline):
model = Emp
form = UpdateYearlyForm
show_change_link = True
class CompanyAdmin(admin.ModelAdmin):
list_display = ('companyname','companyid','get_active', 'get_updated')
inlines = [EmpInline]
When the Company name is clicked on, the company details are shown along with a formset for all the related employees.
This works in regards to displaying the form however one of the fields is a custom choice field which indirectly updated a model field. Which, in the normal user view (this form needs to be used both by an admin for all records and for users for the records pertaining to them) the custom field is handled as below.
I've only shown a snippet of the view as it is quite long.
views.py --
if formset.is_valid():
for form in formset.forms:
if form.is_valid():
obj = form.save(commit=False)
data = form.cleaned_data
if data['updatefield'] == 'accident':
obj.years += 1
else data['updatefield'] == 'free':
obj.years += 1
obj.save()
Is there a way of handling the form (and the custom field) in the same way when used as an inlineform in the admin?
If it helps anyone - overriding the save() function on the form itself sorted this problem and it probably better practice therefore I changed to using this on both the User and Admin side.

Custom django form that has fields populated from database

I am new to Django. I have a custom form that uses forms.Modelform to create a custom form from my model. I have some data in my database already that I manually input for testing.
However, the user and course field shows up as dropdowns. But they do not have any data in the dropdown list. How can I have django to pull data from the database and display the information into each dropdown on my form?
models.py:
class Student(models.Model):
user = models.OneToOneField(User)
course = models.ForeignKey(Course)
view.py:
def home(request):
if request.method == 'GET':
form = StudentForm()
else:
form = StudentForm(request.POST)
if form.is_valid():
pass
return render(request, "request.html", {'form': form}, context_instance=RequestContext(request))
forms.py:
class StudentForm(forms.ModelForm):
class Meta:
model = Student
Update
Actually found out that the changes weren't saved to my DB. They are now loading into the form. However in the dropdown list, it is showing up as "Student Object", and "Course Object"
How can I make it so they show up with proper names?
I would advocate that you move away from doing this if this is testing, and instead follow the guidelines for testing as outlined in the django tutorials, i.e. it creates a fake database for you and you create Users and Courses via Users.objects.create(username=...)

Convert all CharField Form Field inputs to lowercase in Django forms

I am using a Django form for user signup, where the user is able to enter a coupon code. I want all characters entered in the coupon code field to be converted to lowercase. I've tried using .lower() in the save method, in a custom cleaning method, and in a custom validator, but am having no luck with those approaches. Below is my code.
class StripeSubscriptionSignupForm(forms.Form):
coupon = forms.CharField(max_length=30,
required=False,
validators=[validate_coupon],
label=mark_safe("<p class='signup_label'>Promo Code</p>")
def save(self, user):
try:
customer, created = Customer.get_or_create(user)
customer.update_card(self.cleaned_data["stripe_token"])
customer.subscribe(self.cleaned_data["plan"], self.cleaned_data["coupon"].lower())
except stripe.StripeError as e:
# handle error here
raise e
As mentioned above, I've also tried a cleaning method, but this doesn't work either:
def clean_coupon(self):
return self.cleaned_data['coupon'].lower()
The solution is to create a custom form field, which allows you to override the to_python method, in which the raw values from the form fields can then be modified.
class CouponField(forms.CharField):
def to_python(self, value):
return value.lower()
class StripeSubscriptionSignupForm(forms.Form):
coupon = CouponField(max_length=30,
required=False,
validators=[validate_coupon],
label=mark_safe("<p class='signup_label'>Promo Code</p>")
)
Try using a css text-transform with widget in your form like this:
class StripeSubscriptionSignupForm(forms.Form):
coupon = forms.CharField(max_length=30,
required=False,
validators=[validate_coupon],
label=mark_safe("<p class='signup_label'>Promo Code</p>")
widget=TextInput(attrs={'style': 'text-transform:lowercase;'})
)
I came across this problem myself when working on ensuring that the email field in the user model was only saved as lowercase. The advantage to the method I outline below is that you can control the formating of each field in the form - as against the selected answer above, which will convert all fields to lowercase regardless of whether you wish so or not.
The issue for me and I believe for the OP above is that the cleaned values are now indeed in lower case, however the HTML page (the one rendered after the validation and cleaning) shows the pre-cleaned value (i.e. still in uppercase), which would confuse the user. What is happening is that the the form field value is still as per initial data i.e. X#Y.com and the cleaned data is actually x#y.com .
After processing the submitted form:
>>>user_form.cleaned_data['email']
'x#y.com'
and
>>>user_form['email'].value()
'X#Y.com'
The template uses the user_form['email'].value() instead of the value provided by user_form.cleaned_data['email'], so the user thinks his email has been saved in the uppercase form whereas really it has been saved in lowercase.
In such cases, the simplest way to present the user_form back to the client with the cleaned fields appearing in the template is to just reload the saved form directly after saving. As per the following two examples (one saving to the database one not saving to the database).
forms.py
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
"""
UserForm is a simple form to allow a user to change his/her name and email.
"""
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
def clean_email(self):
"""
ensure that email is always lower case.
"""
return self.cleaned_data['email'].lower()
in views.py
def post(self, request):
user_form = UserForm(request.POST, instance=request.user)
if user_form.is_valid():
user_form.save() # using the provided save from Modelform in this case
user_form = UserForm(instance=request.user) # reload the amended user data
return render(request, 'myApp/user_details.html',{'user_form': user_form})
The key line here is in views.py,the user_form = UserForm(instance=request.user), where the form is reloaded. The effect here is to repopulate the form with the cleaned, (and in this case saved) data before it is presented to the user.
Now you can change every charfield in the form to lowercase by having the appropriate clean_fieldname call for those fields.
Note: if you are not interacting with a database (or just donĀ“t wish to reload from the database) you can repopulate the form as follows:
def post(self, request):
user_form = UserForm(request.POST) #gather the post'ed data
if user_form.is_valid():
user_form.process() # process the gathered cleaned data
user_form = UserForm(
{'email': user_form.cleaned_data['email'],
'first_name': user_form.cleaned_data['first_name'],
'last_name': user_form.cleaned_data['last_name'],}
) # reload the form
return render(request, 'myApp/user_details.html',{'user_form': user_form})
As a slight optimization here, you can use the built in check :
if user_form.has_changed():
following on from the is_valid() check (or in conjunction with it) -usually there is no need to save or process a form if nothing has changed on the form.

Categories

Resources