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)
Related
I'm new to programming and my first language/stack is Python and Django. I have figured out how to create a dropdown menu in my Script form that is pointing to a different class "Patient" but I can't figure out how to only show me data that the current user created. I'm confused if I should set this in my models.py, forms.py or in the views.py? Here is what I have that I think should be working but it is not. (Tried setting in the views.py)
Models.py
class Patient(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient_name = models.CharField(max_length=40, unique=True)
def __str__(self):
return self.patient_name
class Script(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, verbose_name='Primary Patient')
So my patient field is my dropdown and it is looking at the Patient class grabbing the patient name string. I only want patient_name entry's that this user created in the dropdown.
Views.py
class ScriptCreateView(LoginRequiredMixin, CreateView):
model = Script
template_name = 'script_new.html'
success_url = reverse_lazy('script_list')
fields = (
'patient',
'drug_name',
'drug_instructions',
'drug_start_day',
'drug_start_time',
'drug_hours_inbetween',
'drug_num_days_take',
)
#This sets user created fields only??
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
author=self.request.user
)
#This sets the author ID in the form
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form
)
Forms.py
class ScriptForm(forms.ModelForm):
class Meta:
model = Script
fields = '__all__'
#This is requiring user login for any of these views??
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if user:
self.fields['patient'].queryset = Patient.objects.filter(author=user)
I'm sure it is my lack of experience here but I thought by setting the function def get_queryset in the view that it would only show me user created data. I have googled a bunch and I really can't find the clear answer on this.
In your views.py file initialize form like this please
<form or form_class> = Form(request.POST, user=request.user)
I had to add the last form.fields query below in the view which filtered items only created by "author" which is what I was looking for:
def get_form(self):
form = super().get_form()
form.fields['drug_start_day'].widget = DatePickerInput()
form.fields['drug_start_time'].widget = TimePickerInput()
form.fields['patient'].queryset = Patient.objects.filter(author=self.request.user)
return form
I have the following model in Django which I use to store data about medicines.
class Medicine(models.Model):
Medicine_Name = models.CharField(max_length=100)
User_Associated = models.ForeignKey(User, on_delete=models.CASCADE)
Tablets_In_Box = models.IntegerField()
Dose_in_mg = models.IntegerField()
Dose_Tablets = models.IntegerField()
Number_Of_Boxes = models.IntegerField()
Last_Collected = models.DateField()
def __str__(self):
return self.Medicine_Name
def get_absolute_url(self):
return reverse('tracker-home')
I am trying to create a model form where a user can update the last collection of one of their medicines. Here is what I began with.
class CollectionForm(forms.ModelForm):
class Meta:
model = Medicine
fields = ['Medicine_Name', 'Number_Of_Boxes', 'Last_Collected']
I do not understand how I can call an instance of my model based on the 'Medicine_Name' from the field. In other words, I need the user to be able to select the correct medicine from a dropdown menu, and then the form must update the 'Last_Collected', and 'Numer_Of_Boxes' fields on my Medicine model.
https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#the-save-method
It seems this contains relevant information, but I struggle to see how to use it in this instance. How can I correctly get the instance of the medicine form I need, based on the user input in the form? Furthermore how can I use the save method in my views to make sure the database gets updated correctly?
EDIT Added view for the form:
def update(request, pk):
instance = Medicine.objects.get(id=pk)
if request.method == 'POST':
form = CollectionForm(user=request.user, instance=instance, data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.User_Associated = request.user
instance.save()
else:
form = CollectionForm()
context = {'form': form}
return render(request, 'tracker/medicine_collection.html', context )
**EDIT
views:
def update(request, pk):
instance = Medicine.objects.get(id=pk)
if request.method == 'POST':
form = CollectionForm(instance=instance, data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.User_Associated = request.user
instance.save()
return redirect ('/')
....
This is based on updating the instance of the specific user. This tutorial helpt me achieve the same thing.
https://youtu.be/EX6Tt-ZW0so
Tried a different approach (class based views - UpdateView) I just learned here on SO. Did not test it but I think its a step in the right direction.
class UpdateMedicine(LoginRequiredMixin, UpdateView):
model = Medicine #call the model you need to update
fields = ['Medicine_Name', 'Number_Of_Boxes', 'Last_Collected'] #specify the fields you need to update
template_name_suffix = 'medicine_update_form' #specify the template where the update form is living
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
user=self.request.user, #get the current logged in user
instance=get_object_or_404(Medicine, pk=self.kwargs['pk']) #get the pk of the instance
)
return context
def form_valid(self, form):
form.instance.medicine = get_object_or_404(Medicine, slug=self.kwargs['pk'])
return super().form_valid(form) #saves the updates to the instance
def get_success_url(self):
return reverse('medicine-collection') #name of the url where your 'tracker/medicine_collection.html is living
Link the appropriate templates and urls to the above example and try some things yourself.
Link to the django docs:
https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-editing/
Good luck!
I know the title says the question has been asked before but the situation is different.
I have something called Agent:
class Agent(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='agents')
...
and a Group:
class Group(models.Model):
agents = models.ManyToManyField('agents.Agent', blank=True, related_name='groups')
now with Django class based views (UpdateView maybe) I want create a view that a user can see only its agents and select only one of them to add it to a specific group.
as far as I get was this
#method_decorator(login_required, name='dispatch')
class GroupAgentRegister(UpdateView):
model = Group
fields = ('agents',)
template_name = 'register.html'
context_object_name = 'group'
def get_form(self, form_class=None):
form = super(GroupAgentRegister, self).get_form(form_class)
form.fields['agents'].queryset = self.request.user.agents.all()
return form
def form_valid(self, form):
if self.object.agents.filter(user=self.request.user):
form.add_error(None, ValidationError(u'Already Registered'))
return super(GroupAgentRegister, self).form_invalid(form)
return super(GroupAgentRegister, self).form_valid(form)
the form rendering is fine except that I'm able to select multiple agents.
but when I select a value and post it it replace the new selected agents with existing ones and it's not appended to the old ones.
I solved it this way. it may help others too.
first I created a form:
class GroupRegistrationForm(forms.ModelForm):
agents = forms.ModelChoiceField(Group.objects.none())
class Meta:
model = Group
fields = ('agents',)
and I changed the register view to this:
#method_decorator(login_required, name='dispatch')
class GroupAgentRegister(UpdateView):
model = Group
form_class = GroupRegistrationForm
fields = ('agents',)
template_name = 'register.html'
context_object_name = 'group'
def get_form(self, form_class=None):
form = super(GroupAgentRegister, self).get_form(form_class)
form.fields['agents'].queryset = self.request.user.agents.all()
return form
def form_valid(self, form):
if self.object.agents.filter(user=self.request.user):
form.add_error(None, ValidationError(u'Already Registered'))
return super(GroupAgentRegister, self).form_invalid(form)
self.object.agents.add(form.cleaned_data['agents'])
self.object.save()
return HttpResponseRedirect(self.get_success_url())
and everything works fine with the most minimal change I had to apply.
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.
Lets say I have a model like the following:
class Patient(models.Model):
#has some fields
doctor = models.ForeignKey(User)
So a patient model has a doctor that treats him. I also have model form
class PatientForm(ModelForm):
class Meta:
model=Patient
So all fields are included. Lets say now frontdesk(nonstaff) can create patients and has to asign a doctor on them. So PatientForm works cause the doctor field will appear normally. But a doctor can create a patient too, but when a doctor(staff user) creates a patient the doctor field must be filled automatically by the connected user and not rendered in the template. Something like:
if request.user.is_staff():
form = #PatientForm_without_doctor_field
else:
form = PatientForm()
Can it be done using the same PatientForm class? Is it better to have to different ModelForms (one with field included and one with doctor excluded) and use it accordingly? which is the best approach for that? Is it better to have a new model form with request.user in the constructor?
Pass request.user to form init in view:
def some_view(request):
form = PatientForm(user=request.user)
and modify form fields according to user in form init after form creation:
from django.contrib.auth.models import AnonymousUser
class PatientForm(ModelForm):
class Meta:
model=Patient
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', AnonymousUser())
super(PatientForm, self).__init__(*args, **kwargs)
if user.is_staff():
# hide field and set doctor to request.user
self.fields['doctor'].widget = forms.HiddenInput()
self.fields['doctor'].initial = user
[Edit] While initializing you can set the initial values based on a keyword argument:
class PatientForm(ModelForm):
def __init__(self, readonly_doctor=None, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
if readonly_doctor is not None:
self.fields['doctor'].widget.attrs['readonly'] = True
self.fields['doctor'].initial = readonly_doctor
class Meta:
model=Patient
Then you can conditionally pass it:
if request.user.is_staff():
form = PatientForm(readonly_doctor=request.user)
else:
form = PatientForm()