I am trying to build custom django form for changing username and user email for an application. That's why I need to pass the user details from the session to a form in order to check if the logged user exists. I am doing in this way:
in views.py
personal_info_form = PersonalInfoForm(prefix='personal_info',
user_details=user_details)
where user_details is a dictionary:
'user_details': [{'username': u'username',
'registration_date': datetime.date(2009, 10, 22),
'id': 13, 'email': u'user#mail.com'}]}
In forms.py I have the following code:
class PersonalInfoForm(forms.Form):
def __init__(self, *args, **kwargs):
user_details = kwargs.pop('user_details', None)
super(PersonalInfoForm, self).__init__( *args, **kwargs)
username = forms.CharField(required=True, initial=user_details[0]['username'])
email = forms.EmailField(required=True)
And I get the following error:
name 'user_details' is not defined
I tried accessing it with self.user_details and only user_details and it gives me the same error
user_details is passed to __init__, so is not defined outside of it. That's why you can't access it when you're instatiating that CharField object. Set initial in __init__ itself, after you've popped it from kwargs, for instance:
class PersonalInfoForm(forms.Form):
username = forms.CharField(required=True)
email = forms.EmailField(required=True)
def __init__(self, *args, **kwargs):
user_details = kwargs.pop('user_details', None)
super(PersonalInfoForm, self).__init__(*args, **kwargs)
if user_details:
self.fields['username'].initial = user_details[0]['username']
When you get a chance, consider reading up on scopes in python.
Can you explain in a more clear way what you are trying to do? I believe what you are doing is unnecessary. Not sure if I understood you, but take a look at Django Model Forms: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/
>>> from django.forms import ModelForm
# Create the form class.
>>> class ArticleForm(ModelForm):
... class Meta:
... model = Article
# Creating a form to add an article.
>>> form = ArticleForm()
# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(instance=article)
If it's what you are looking for, you could easily addapt to use your user model. Remember, you can pick which fields you want to allow to be changed. The instance parameter sets the initial field values (which is what I understood you want to do). The example above will show all fields, the below example shows how to display only 2 fields.
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
Hope I've helped.
Related
Good day SO.
I want to ask something basic. I tried on my end to update my data from html to save to database but to no avail. My Model structure is Account extends to AbstractBaseUser, BaseUserManager and CompanyAccount links to Account by OneToOneField. I can say that my Model should have no problem since I can Register Account and Company Account without any problems.
EDIT
Now I can save my CompanyAccount but the link to Account was removed.
Still trying to check which is the problem
views.py
#login_required(login_url='/login/')
def user_my_page(request, *args, **kwargs):
context = {}
context['form_type'] = 'update'
context['account_type'] = request.user.account_type
if request.POST:
if request.user.account_type == 1:
company = CompanyAccount.objects.get(account=request.user)
account_form = CompanyAccountForm(request.POST, instance=company)
if account_form.is_valid():
account_form.save()
print(request.POST)
print('SAVE')
else:
print('ERROR')
forms.py
class AccountCreationForm(UserCreationForm):
accuser_id = forms.CharField(max_length=10, help_text="Required")
class Meta:
model = Account
fields = ("accuser_id", "password1", "password2")
class CompanyAccountForm(forms.ModelForm):
class Meta:
model = CompanyAccount
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['company_name'].widget.attrs.update({'class': 'content-input'})
self.fields['company_name_kana'].widget.attrs.update({'class': 'content-input'})
print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['6zrwt68PNiJKrgZcKanDcVJkqAtogbQbNk2wHwjOzg7ybfq3Lyei9ZqdbmAJcYrV'], 'company_name': ['TESTING'], ....etc
This part here, it does print SAVE on my terminal but it does not save to the database.
I got it. Created a new Form for update then excluded the Account.
class CompanyAccountUpdateForm(forms.ModelForm):
class Meta:
model = CompanyAccount
fields = "__all__"
exclude = ('account',)
Then used the new Form to my update view
I am facing the following scenario: I have a Django model class called Contact, which looks something like:
class Contact(models.Model):
first_name = models.CharField(max_length=70)
last_name = models.CharField(max_length=70)
company = models.ForeignKey(Company) // should be disabled in user-facing forms
organizations = models.ManyToManyField(Organization) // should be disabled and hidden in user-facing forms
// some other fields not relevant to this question
Both users of the app and administrators should be able to create objects of type Contact and store it in the database. However, for a user this should be restricted in the way that he cannot freely chose the company field of a Contact object. For this, I have created a base ModelForm called ContactForm, intended to be used by administrators, and a restricted user-facing child class called RestrictedContactForm. The code looks as follows:
class ContactForm(forms.modelForm):
class Meta:
model = Contact
fields = ['first_name', 'last_name', 'company', 'organizations']
class RestrictedContactForm(ContactForm):
class Meta(ContactForm.Meta):
widgets = {'organizations': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super(RestrictedContactForm, self).__init__(*args, **kwargs)
// Maybe populate company and organization here somehow?
self.fields['company'].disabled = True
self.fields['organization'].disabled = True
The RestrictedContactForm is rendered to the user once he decides to create a new contact. Clearly, as both the company and organization fields are mandatory, they need to be manually injected somehow. It is exactly here where my problem lies: I haven't managed to populate these fields by hand.
Below you can find an outline of the view function implementing the logic of a user initiated creation.
def create_contact(request, company_pk):
company = Company.objects.get(pk=company_pk)
organization = Organization.objects.get(...)
if request.method == 'POST':
// Add company.pk and organization.pk to POST here?
// Pass data dictionary manually populated from POST and
// with company.pl and organization.pk to constructor?
contact_form = RestrictedContactForm(request.POST, request.FILES)
// Add company.pk and organization.pk to contact_form.data
// here (after making it mutable)?
if contact_form.is_valid():
contact_form.save()
return redirect(...)
return render(...)
contact_form = ContactForm(initial={'company': company, 'organizations': organization})
I have already tried every suggestion appearing in the comments above. The form simply never validates. My question hence is, what would be the correct way of doing this? Moreover, is the approach outlined at least conceptually right?
The project uses Django 1.9.
If the company and organization fields are not changeable by the user, then they should not be included in the fields list at all in RestrictedContactForm.
What you can do instead is pass the known values for organization and company into the constructor of the form, and then assign them to the object before you actually create it in the database.
class RestrictedContactForm(ContactForm):
class Meta(ContactForm.Meta):
fields = ['first_name', 'last_name', ]
def __init__(self, company, organization, *args, **kwargs):
super(RestrictedContactForm, self).__init__(*args, **kwargs)
self.company = company
self.organization = organization
def save(self, commit=True):
instance = super(RestrictedContactForm, self).save(commit=False)
if not instance.pk:
instance.company = self.company
instance.organization = self.organization
if commit:
instance.save()
return instance
def create_contact(request, company_pk):
# ...
if request.method == 'POST':
company = Company.objects.get(pk=company_pk)
organization = company.organization
contact_form = RestrictedContactForm(company, organization, request.POST, request.FILES)
# ...
# ...
I've always done this using the form_valid method. In this case, in the form_valid method of the child form:
def form_valid(self, form):
form.instance.company = foo
form.instance.organisation = bar
return super().form_valid(form)
This populates the missing field, and then saves the form.
I'm trying to set the value of a Django field inside of the Form class. Here is my model
class Workout(models.Model):
user = models.ForeignKey(User , db_column='userid')
datesubmitted = models.DateField()
workoutdate = models.DateField();
bodyweight = models.FloatField(null=True);
workoutname = models.CharField(max_length=250)
Here is the form class, in which i am attempting to achieve this:
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
def __init__(self,*args, **kwargs):
# this is obviously wrong, I don't know what variable to set self.data to
self.datesubmitted = self.data['datesubmitted']
Ok, sorry guys. I'm passing the request.POST data to the WorkoutForm in my view like this
w = WorkoutForm(request.POST)
However, unfortunately the names of the html elements have different names then the names of the model. For instance, there is no date submitted field in the html. This is effectively a time stamp that is produced and saved in the database.
So I need to be able to save it inside the form class some how, I think.
That is why I am trying to set the datesubmitted field to datetime.datetime.now()
Basically I am using the form class to make the verification easier, and I AM NOT using the form for it's html output, which I completely disregard.
You have to do that in the save method of your form
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
def __init__(self,*args, **kwargs):
super(WorkoutForm, self).__init__(*args, **kwargs)
def save(self, *args, **kw):
instance = super(WorkoutForm, self).save(commit=False)
instance.datesubmitted = datetime.now()
instance.save()
How ever you can set that in your model also to save the current datetime when ever a new object is created:
datesubmitted = models.DateTimeField(auto_now_add=True)
You can set some extra values set in form as:
form = WorkOutForm(curr_datetime = datetime.datetime.now(), request.POST) # passing datetime as a keyword argument
then in form get and set it:
def __init__(self,*args, **kwargs):
self.curr_datetime = kwargs.pop('curr_datetime')
super(WorkoutForm, self).__init__(*args, **kwargs)
You should not be using a ModelForm for this. Use a normal Form, and either in the view or in a method create a new model instance, copy the values, and return the model instance.
I'm using Django profiles and was inspired by James Bennett to create a dynamic form (http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/ )
What I need is a company field that only shows up on my user profile form when the user_type is 'pro'.
Basically my model and form look like:
class UserProfile(models.Model):
user_type = models.CharField(...
company_name = models.CharField(...
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ('company_name',)
And I add the company_name field in init like James Bennett showed:
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args,**kwargs)
if (self.instance.pk is None) or (self.instance.user_type == 'pro'):
self.fields['company_name'] = forms.CharField(...
The problem is that, when I try to save() an instance of UserProfileForm, the field 'company_name' is not saved...
I have gone around this by calling the field explicitly in the save() method:
def save(self, commit=True):
upf = super(UserProfileForm, self).save(commit=False)
if 'company_name' in self.fields:
upf.company_name = self.cleaned_data['company_name']
if commit:
upf.save()
return upf
But I am not happy with this solution (what if there was more fields ? what with Django's beauty ? etc.). It kept me up at night trying to make the modelform aware of the new company_name field at init .
And that's the story of how I ended up on stackoverflow posting this...
I would remove this logic from form and move it to factory. If your logic is in factory, you can have two forms:
UserProfileForm
ProUserProfileForm
ProUserProfileForm inherits from UserProfileForm and changes only "exclude" constant.
You will have then following factory:
def user_profile_form_factory(*args, instance=None, **kwargs):
if (self.instance.pk is None) or (self.instance.user_type == 'pro'):
cls = ProUserProfileForm
else:
cls = UserProfileForm
return cls(*args, instance, **kwargs)
It seems I found a solution:
def AccountFormCreator(p_fields):
class AccountForm(forms.ModelForm):
class Meta:
model = User
fields = p_fields
widgets = {
'photo': ImageWidget()
}
return AccountForm
#...
AccountForm = AccountFormCreator( ('email', 'first_name', 'last_name', 'photo', 'region') )
if request.POST.get('acforms', False):
acform = AccountForm(request.POST, request.FILES, instance=request.u)
if acform.is_valid():
u = acform.save()
u.save()
ac_saved = True
else:
acform = AccountForm(instance = request.u)
When are you expecting the user_type property to be set? This seems like something that should be handled by javascript rather than trying to do funny things with the model form.
If you want the company_name field to appear on the client after they've designated themselves as a pro, then you can 'unhide' the field using javascript.
If instead, they've already been designated a pro user, then use another form that includes the company_name field. You can sub-class the original model form in the following manner.
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ('company_name',)
class UserProfileProForm(UserProfileForm):
class Meta:
exclude = None # or maybe tuple() you should test it
Then in your view, you can decide which form to render:
def display_profile_view(request):
if user.get_profile().user_type == 'Pro':
display_form = UserProfileProForm()
else:
display_form = UserProfileForm()
return render_to_response('profile.html', {'form':display_form}, request_context=...)
This would be the preferred way to do it in my opinion. It doesn't rely on anything fancy. There is very little code duplication. It is clear, and expected.
Edit: (The below proposed solution does NOT work)
You could try changing the exclude of the meta class, and hope that it uses the instances version of exclude when trying to determine whether to include the field or not. Given an instance of a form:
def __init__(self, *args, **kwargs):
if self.instance.user_type == 'pro':
self._meta.exclude = None
Not sure if that will work or not. I believe that the _meta field is what is used after instantiation, but I haven't verified this. If it doesn't work, try reversing the situation.
def __init__(self, *args, **kwargs):
if self.instance.user_type != 'pro':
self._meta.exclude = ('company_name',)
And remove the exclude fields altogether in the model form declaration. The reason I mention this alternative, is because it looks like the meta class (python sense of Meta Class) will exclude the field even before the __init__ function is called. But if you declare the field to be excluded afterwards, it will exist but not be rendered.. maybe. I'm not 100% with my python Meta Class knowledge. Best of luck.
What about removing exclude = ('company_name',) from Meta class? I'd think that it is the reason why save() doesn't save company_name field
I have a read-only field in a django form that I sometimes want to edit.
I only want the right user with the right permissions to edit the field. In most cases the field is locked, but an admin could edit this.
Using the init function, I am able to make the field read-only or not, but not optionally read-only. I also tried passing an optional argument to StudentForm.init but that turned much more difficult that I expected.
Is there a proper way to do accomplish this?
models.py
class Student():
# is already assigned, but needs to be unique
# only privelidged user should change.
student_id = models.CharField(max_length=20, primary_key=True)
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30)
# ... other fields ...
forms.py
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ('student_id', 'last_name', 'first_name',
# ... other fields ...
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance:
self.fields['student_id'].widget.attrs['readonly'] = True
views.py
def new_student_view(request):
form = StudentForm()
# Test for user privelige, and disable
form.fields['student_id'].widget.attrs['readonly'] = False
c = {'form':form}
return render_to_response('app/edit_student.html', c, context_instance=RequestContext(request))
Is that what you are looking for? By modifying your code a little bit:
forms.py
class StudentForm(forms.ModelForm):
READONLY_FIELDS = ('student_id', 'last_name')
class Meta:
model = Student
fields = ('student_id', 'last_name', 'first_name')
def __init__(self, readonly_form=False, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
if readonly_form:
for field in self.READONLY_FIELDS:
self.fields[field].widget.attrs['readonly'] = True
views.py
def new_student_view(request):
if request.user.is_staff:
form = StudentForm()
else:
form = StudentForm(readonly_form=True)
extra_context = {'form': form}
return render_to_response('forms_cases/edit_student.html', extra_context, context_instance=RequestContext(request))
So the thing is to check permissions on the views level, and then to pass argument to your form when it is initialized. Now if staff/admin is logged in, fields will be writeable. If not, only fields from class constant will be changed to read only.
It would be pretty easy to use the admin for any field editing and just render the student id in the page template.
I'm not sure if this answers your questions though.