I have 2 models linked by a OneToOneField :
class User(models.Model):
name = models.CharField(max_length=100, unique=True)
email = models.EmailField(blank=True)
base = models.OneToOneField(BaseUser)
...
class BaseUser(models.Model):
enabled = models.BooleanFiled(default=True)
...
I use a ModelForm to display the user's attributes :
class UserForm(ModelForm):
class Meta:
model = User
fields = ['name', 'email', ...]
And in my views :
class UserCreate(generic.CreateView):
model = User
template_name = 'user/create.html'
success_url = reverse_lazy('users:list')
form_class = UserForm
I would like to change the enabled attribute in the user's template, but I have just access to the User's fields (name, email...).
How can I make it please ?
You can render 2 forms in your template:
def user_create(request):
if request.method == "POST":
user_form = UserForm(data=request.POST)
baseuser_form = BaseUserForm(data=request.POST)
if user_form.is_valid() and baseuser_form.is_valid():
base_user = baseuser_form.save()
user = user_form.save(commit=False)
user.base = base_user
user.save()
return redirect(reverse_lazy('users:list'))
else:
....
else:
user_form = UserForm()
baseuser_form = BaseUserForm()
return render_to_response('user/create.html', {'user_form': user_form, 'baseuser_form': baseuser_form})
If you only have one additional field you want to add, you could add it to the UserForm.
class UserForm(ModelForm):
enabled = forms.BooleanField(default=True)
class Meta:
model = User
fields = ['name', 'email', ...]
Then, in your form_valid method you can set the value for the base_user. Since it's a create view, I'm assuming you have to create it first.
class UserCreate(generic.CreateView):
def form_valid(self, form):
base_user = BaseUser.objects.create(
enabled=form.cleaned_data['enabled']
)
form.instance.base = base_user
return super(UserCreate, self).form_valid(form)
If you want to add more than one extra field, then you probably want separate forms for BaseUser and User. In this case, extending CreateView gets a bit tricky, and it might be simpler to use a function based view like in Rohit's answer.
Related
I am trying to associate the user with the post. I have two models students is for user and sublists is for user posts with a foreign key(author). I am using MySQL database and using forms to store data into them. when my form.author execute in my HTML file it gives me a list of ids for all users in the databse but I am already logged in and i want to post as the logged in user without choosing. If remove it says my form is not valid which make sense since im not inputing for form.author.Since I'm using MySQL, I'm not using the built-in User authentication method, but instead comparing both email and password with the login form input. Spend too much time on this but hard to get around with this one. Any help would be appreciated
my views.py look like this
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
try:
form.save()
messages.success(request, ' Subscirption Saved')
name = sublist.objects.get(name=name)
return render (request, 'subscrap/main.html', {'sublist': name})
except:
pass
else:
messages.success(request, 'Error')
pass
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
#login_required(login_url='login')
#cache_control(no_cache=True, must_revalidate=True, no_store=True)
def main(request):
return render(request, 'subscrap/main.html')
def mod(request):
student = students.objects.all()
return render(request, 'subscrap/mod.html' , {'students': student})
My Models.py
class students(models.Model):
fname = models.CharField(max_length=50)
lname = models.CharField(max_length=50)
password = models.CharField(max_length = 50 , null = True)
passwordrepeat = models.CharField(max_length = 50, null = True)
email = models.EmailField(max_length=150)
class Meta:
db_table = "students"
class sublist(models.Model):
author = models.ForeignKey(students, related_name='sublist' ,on_delete=models.CASCADE)
name = models.CharField(max_length=150)
cost = models.IntegerField(default = 0)
renewalcycle = models.IntegerField(default = 0)
class Meta:
db_table = "sublist"
Since I'm using forms here's my forms.py
lass StudentForm(forms.ModelForm):
class Meta:
model = students
fields = "__all__"
class Studentlogin(forms.Form):
email = forms.EmailField(max_length=150)
password = forms.CharField(max_length = 50, widget=forms.PasswordInput)
class Sublist(forms.ModelForm):
class Meta:
model = sublist
fields = "__all__"
Exclude the Author from the Sublist form:
class Sublist(forms.ModelForm):
class Meta:
model = sublist
exclude = ['author']
In the addnew method, you associate the .instance.author with the request.user:
#login_required(login_url='login')
def addnew(request):
if request.method == 'POST':
form = Sublist(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
messages.success(request, ' Subscirption Saved')
return redirect('some_view')
else:
messages.error(request, 'Error')
else:
form = Sublist()
return render(request, 'subscrap/addnew.html', {'form': form})
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from sublist to Sublist.
Note: Usually a Form or a ModelForm ends with a …Form suffix,
to avoid collisions with the name of the model, and to make it clear that we are
working with a form. Therefore it might be better to use SublistForm instead of
Sublist.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the students directly. For more information you can see the referencing the User model section of the documentation.
I have a model
class someModel(models.Model):
.....
.....
user = models.OneToOneField(User, on_delete=models.CASCADE)
#User is the field that I need from the form
.....
.....
Here I have fields that have to be filled and the user field that is connected to the users table
In my forms
class someForm(forms.ModelForm):
class Meta:
model = someModel
fields = [....., 'user']
widgets = { .... }
I want to pass the user from my view but I don't know how to do that and I cant find it online. Form fails because the user is not passed.
#login_required
def someView(request):
organisator= request.user
if request.method == "POST":
cl_form = SomeModel(data=request.POST, initial={'user': organisator})
if cl_form.is_valid():
cl_form.save(commit=True)
else:
print(cl_form.errors)
return HttpResponseRedirect(reverse('someviewfromurls'))
else:
cl_form = someForm(initial={'user':organisator})
return render(request, 'somefile.html', context=
{"cl_form": cl_form})
You can try like this:
if cl_form.is_valid():
instance = cl_form.save(commit=False)
instance.user = request.user
instance.save()
I want to change select option's label by user status.
my model is
class BookCategory(model):
label_1 = char
label_2 = char
def __unicode__(self):
#something here?
class Book(model):
name = char
categoey = models.Foreignkey(BookCategory)
BookCategory is used in createview for new book, and the page has modelform, textinput for book.name and choices for book.catgory.
My goal is
if user type==1:
=>display category's label_1
if user type==2:
=>display category's label_2
I know "__unicode__" can display instance's value, but I want to know change its field by user's status.
Anyone knows the solution?
add:
my view and modelform are so simple.
view is
class CreateBook(CreateView):
template_name = "template.html"
model = Book
form_class = BookForm
success_url = "success_url"
def form_valid(self, form):
if form.is_valid():
form.save()
and form is
class LessonForm(ModelForm):
class Meta:
model = Book
fields = ('name',
'category',
)
widgets = {
'name': forms.Textarea(attrs={'class': 'text_box'}),
'category': forms.Textinput(attrs={'class': 'select_box'}),
}
Subclass ModelChoiceField to create two custom models fields. Override label_from_instance to customize how you want the object to be displayed in the form.
class Type1CategoryField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.label1
class Type2CategoryField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.label1
Then change your form so that it takes user as a keyword argument, and change the category field to use the appropriate model choice field.
class LessonForm(ModelForm):
class Meta:
model = Book
fields = ('name',
'category',
)
widgets = {
'name': forms.Textarea(attrs={'class': 'text_box'}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(LessonForm, self).__init__(*args, **kwargs)
if user.user_type == 1:
self.fields['category'] = Type1CategoryField(queryset=Category.objects.all())
elif user.user_type == 2:
self.fields['category'] = Type2CategoryField(queryset=Category.objects.all())
It doesn't make sense to use forms.Textinput for a choice field, so I removed category from your widgets.
Finally, modify your view so that it includes the logged-in user in the form kwargs. Use LoginRequiredMixin to make sure that only logged-in users can access the view.
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateBook(LoginRequiredMixin, CreateView):
template_name = "template.html"
model = Book
form_class = BookForm
success_url = "success_url"
def get_form_kwargs(self):
kwargs = super(CreateBook, self).get_form_kwargs()
kwargs['user'] = request.user
What I'm trying to do is something like this:
1) I've created model
class Example(models.Model):
username = models.CharField(max_lenght=111)
pass = models.CharField(max_lenght=111)
2)I've created form using ModelForm, and added one extra field
class ExampleForm(ModelForm):
extra_field= form.CharField(max_length=333)
class Meta:
model = Client
fields = ['username', 'pass']
3)I've created view to handle this form
class Registration(CreateView):
"""
View handles user registration.
"""
form_class = ExampleForm
model = Example
template_name = 'accounts/registration.html'
success_url = reverse_lazy('accounts:registered')
Now what I would like to do is do some custom processing of extra_field. I figured out this should be done in save method of ExampleForm. For example:
def save(self, commit=True):
user = super(ExampleForm, self).save(commit=False)
data = self.cleaned_data['extra_field']
user.set_password(self.cleaned_data['pass'] + self.cleaned_data['extra_field'])
if commit:
user.save()
return user
But this doesn't work.
Is this the right way to handle this kind of situation, or there is better way?
Biggest problem is that this isn't my code, so I should only change ExampleForm.
Is there way to do this?
Thanks in advance.
Best Regards,
Nikola
You can do this in the form_valid() method of the CreateView.
class Registration(CreateView):
"""
View handles user registration.
"""
form_class = ExampleForm
model = Example
template_name = 'accounts/registration.html'
success_url = reverse_lazy('accounts:registered')
def form_valid(self, form):
example = form.save(commit=False)
example.extra_field = derive_data(form.cleaned_data)
example.save()
return super(Registration, self).form_valid(form)
I'm having a problem with logged users and a Django ModelForm. I have a class named _Animal_ that has a ForeignKey to User and some data related to the animal like age, race, and so on.
A user can add Animals to the db and I have to track the author of each animal, so I need to add the request.user that is logged when the user creates an animal instance.
models.py
class Animal(models.Model):
name = models.CharField(max_length=300)
age = models.PositiveSmallIntegerField()
race = models.ForeignKey(Race)
...
publisher = models.ForeignKey(User)
def __unicode__(self):
return self.name
class AnimalForm(ModelForm):
class Meta:
model = Animal
The main goal is hide the publisher field in the form, and submit the logged user when hitting save button.
I can catch the current user in the view using initial, but what I also want is not display the field.
views.py
#login_required
def new_animal(request):
if request.method == "POST":
form = AnimalForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
else:
form = AnimalForm(initial={'publisher': request.user})
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
You just need to exclude it from the form, then set it in the view.
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
... and in the view:
form = AnimalForm(request.POST)
if form.is_valid():
animal = form.save(commit=False)
animal.publisher = request.user
animal.save()
(Note also that the first else clause - the lines immediately following the redirect - is unnecessary. If you leave it out, execution will fall through to the two lines at the end of the view, which are identical.)
Another way (slightly shorter):
You need to exclude the field as well:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
then in the view:
animal = Animal(publisher=request.user)
form = AnimalForm(request.POST, instance=animal)
if form.is_valid():
animal.save()
I would add it directly to the form:
class AnimalForm(ModelForm):
class Meta:
model = Animal
exclude = ('publisher',)
def save(self, commit=True):
self.instance.publisher = self.request.user
return super().save(commit=commit)
This is in my opinion the cleanest version and you may use the form in different views.
If you are using ModelAdmin
you should add method get form on your ModelAdmin
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
form.request = request
return from
and you can now access request in your ModelForm
class ProductAdminForm(forms.ModelForm):
def save(self, commit: bool, *args, **kwargs):
self.instance.user = self.request.user
return super().save(commit=commit)
pass