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.
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 a multiple modelforms form multiple model. I want one single CreateView for submitting all the values. I have three models(Employee, WorkExperience and Education). Models are connected using ForeignKey with each other.
forms.py:
class EmployeeAddModelForm(forms.ModelForm):
"""
Creates a form for employee invitations
"""
class Meta:
model = Employee
fields = [
'e_id',
'first_name',
'last_name',
'gender',
'religion',
]
class WorkExperienceForm(forms.ModelForm):
"""
Creates a form for saving employee work experiences
"""
class Meta:
model = WorkExperience
fields = [
'previous_company_name',
'job_designation',
'from_date',
'to_date',
'job_description',
]
class EducationForm(forms.ModelForm):
"""
Creates a form for saving educational info of an employee
"""
class Meta:
model = Education
fields = [
'institution_name',
'degree',
'passing_year',
'result',]
I have three model forms from three models in form.py. I want that my createview inherits all this modelforms and create a single form for posting data.
views.py:
class EmployeeAddView(LoginRequiredMixin,CreateView):
"""
Creates new employee
"""
login_url = '/authentication/login/'
template_name = 'employee/employee_add_form.html'
form_class = EmployeeAddModelForm
work_form_class = WorkExperienceForm
queryset = Employee.objects.all()
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
work_form = self.work_form_class(request.POST, prefix='work_form')
education_form = self.education_form_class(request.POST, prefix='education_form')
if form.is_valid() and work_form.is_valid():
instance = form.save()
work = work_form.save(commit=False)
education = education_form.save(commit=False)
work.employee = instance
education.employee = instance
work.save()
education.save()
if not education_form.is_valid():
print("Education")
return redirect('employee:employee-list')
def get_success_url(self):
return reverse('employee:employee-list')
I am rendering two forms from my view class. But when I use 'work_form' in my template.html, nothing appears.
How can I render all modelforms in my view?
override get function, because get request can not get work_form in default
def get(self, request, *args, **kwargs):
form = self.form_class(**self.get_form_kwargs())
work_form = self.work_form_class(prefix='work_form')
return render(request, self.template_name, {'form': form, 'work_form': work_form})
I am trying to use inlineformset_factory to create instances of the same model.
models.py
class Skill(models.Model):
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, related_name="employee_skills")
technology = models.ForeignKey(Technology, on_delete=models.CASCADE)
year = models.CharField('common year using amount ', max_length=4)
last_year = models.CharField('Last year of technology using ', max_length=4)
level = models.CharField("experience level", max_length=64, choices=LEVELS)
class Techgroup(models.Model):
""" Group of technology """
name = models.CharField('group_name', max_length=32, unique=True)
class Technology(models.Model):
"""Technologies."""
name = models.CharField('technology name', max_length=32, unique=True)
group = models.ForeignKey(Techgroup, on_delete=models.CASCADE, related_name="group")
In the Administrator pane I created 2 instances of the Techgroup model:
- Framework
- Programming language
All Skill models belong to one of two groups. On the front I display 2 forms, one containing queryset with instances belonging to the Framework, the other with instances belonging to the Programming language.
I divide Querysets using ModelsForm:
forms.py
class SkillBaseCreateForm(forms.ModelForm):
YEAR_CHOICES = [(r, r) for r in range(1, 11)]
LAST_YEAR_CHOICES = [(r, r) for r in range(2015, datetime.datetime.now().year + 1)]
year = forms.CharField(
widget=forms.Select(choices=YEAR_CHOICES),
)
last_year = forms.CharField(widget=forms.Select(choices=LAST_YEAR_CHOICES))
class Meta:
model = Skill
fields = ['technology', 'level', 'last_year', 'year']
class SkillCreatePLanguageForm(SkillBaseCreateForm):
def __init__(self, *args, **kwargs):
super(SkillCreatePLanguageForm, self).__init__(*args, **kwargs)
self.fields['technology'].queryset = Technology.objects.filter(group__name="Programming language")
class SkillCreateFrameworkForm(SkillBaseCreateForm):
def __init__(self, *args, **kwargs):
super(SkillCreateFrameworkForm, self).__init__(*args, **kwargs)
self.fields['technology'].queryset = Technology.objects.filter(group__name="Framework")
SkillFrameworkFormSet = inlineformset_factory(Employee, Skill, form=SkillCreateFrameworkForm, extra=1, can_delete=False)
SkillPLanguageFormSet = inlineformset_factory(Employee, Skill, form=SkillCreatePLanguageForm, extra=1, can_delete=False)
views.py
class SkillTestCreateView(AuthorizedMixin, CreateView):
"""
Create new skill instances
"""
template_name = 'edit.html'
model = Employee
form_class = EmployeeEditForm
def get(self, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = Employee.objects.get(pk=self.kwargs['pk'])
form_class = self.get_form_class()
form = self.get_form(form_class)
form_framework = SkillFrameworkFormSet()
form_language = SkillPLanguageFormSet()
return self.render_to_response(
self.get_context_data(form=form,
form_framework=form_framework,
form_language=form_language))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = Employee.objects.get(pk=self.kwargs['pk'])
form_class = self.get_form_class()
form = self.get_form(form_class)
form_framework = SkillFrameworkFormSet(self.request.POST)
form_language = SkillPLanguageFormSet(self.request.POST)
if (form.is_valid() and form_framework.is_valid() and
form_language.is_valid()):
return self.form_valid(form, form_framework, form_language)
else:
return self.form_invalid(form, form_framework, form_language)
def form_valid(self, form, form_framework, form_language):
"""
Called if all forms are valid. Creates a Employee instance along with
associated models and then redirects to a
success page.
"""
self.object = form.save()
form_framework.instance = self.object
form_framework.save()
form_language.instance = self.object
form_language.save()
return HttpResponseRedirect(reverse_lazy('profile', args=[self.kwargs['pk']]))
def form_invalid(self, form, form_framework, form_language):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
form_framework=form_framework,
form_language=form_language,
))
The problem is that I always get an error message when I submit a form:
Select a valid choice. That choice is not one of the available choices.
The problem is that when I submit a form, I always get an error message in the technology field of the queryset which is displayed first in the template.
That is, if the template
{form_framework}}
{form_language}
a mistake on
queryset = Technology.objects.filter(group__name="Framework"
if
{form_language}
{form_framework}}
a mistake on
queryset = Technology.objects.filter(group__name="Programming language"
If I leave only one form in views.py, everything starts to work.
I've been trying to figure it out for 2 days and I think I'm at a dead end. Need help!
I have this app where I can upload a file to a specific category or subcategory. It works fine but the problem I'm having is when I'm trying to display select values only for a specific user and for a specific parent category it just shows me all the values stored in the database.
views.py
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
login_url = reverse_lazy('users:login')
form_class = FileUploadForm
template_name = 'docman/forms/add-document.html'
success_url = reverse_lazy('docman:index')
success_message = 'Document was successfully added'
def form_valid(self, form):
profile = form.save(commit=False)
profile.user = self.request.user
return super(AddDocumentView, self).form_valid(form)
forms.py
class FileUploadForm(forms.ModelForm):
file = forms.FileField()
class Meta:
model = Document
exclude = ('user',)
fields = [
'file',
'slug',
'category',
]
def __init__(self, user=None, **kwargs):
super(FileUploadForm, self).__init__(**kwargs)
if user:
self.fields['category'].queryset = Category.objects.filter(user_id=user.id, parent_id=None)
I've tried the solutions to the similar questions which is how I even got this far, but it's still not filtering by the user and I can't figure out how to get it to filter by the parent id either. Any ideas to what I'm doing wrong? Any help is appreciated, and I can provide more information if needed.
-----------------SOLUTION UPDATE-----------------
Thanks #solarissmoke I was able to get the user information to the form. Then I just did the same thing to capture the parent_id from the url using kwargs.
views.py
# Override the view's get_form_kwargs method to pass the user and/or pk to the form:
def get_form_kwargs(self):
pk = self.kwargs['pk']
kwargs = super(AddDocumentView, self).get_form_kwargs()
kwargs['user'] = self.request.user
# Check if category exists with pk, otherwise none
if Category.objects.filter(parent_id=pk):
kwargs['pk'] = pk
else:
kwargs['pk'] = None
return kwargs
Then I added the extra agument(pk) to init
forms.py
def __init__(self, user=None, pk=None, **kwargs):
super(FileUploadForm, self).__init__(**kwargs)
if user:
self.fields['category'].queryset = Category.objects.filter(user=user, parent_id=pk)
Your form is expecting a user argument, but you aren't supplying one, so user is always None. You can override the view's get_form_kwargs method to pass the user to the form:
class AddDocumentView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
def get_form_kwargs(self):
kwargs = super(AddDocumentView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Your FileUploadForm will now get the user object and will filter results accordingly.
I have this model:
class Post(models.Model):
thread = models.ForeignKey(Thread)
post_title = models.CharField(max_length=50, blank=True)
# other attributes
And I have a view:
class ThreadView(CreateView):
model = models.Post
template_name = 'forum/thread.html'
fields = ['post_title', 'author', 'post_text']
When I try to send the form I get IntegrityError: NOT NULL constraint failed: forum_post.thread_id.
I think, it's because I foreign key remains empty, but I don't know how to add it automatically.
First, the name of the view you have is not quiet obvious, cause you are trying to create an instance of a Post not of a Thread. Won't it be better to rename it to PostCreateView?
Speaking about the error you get, you are right about foreign key - it is empty. After all, you do not set it anywhere. You should either send it in the form or assign it on validation. The second way is what you are looking for:
class ThreadView(CreateView):
model = models.Post
template_name = 'forum/thread.html'
fields = ['post_title', 'author', 'post_text']
def dispatch(self, *args, **kwargs):
self.thread = get_object_or_404(Thread, pk=kwargs['thread_id'])
return super(ThreadView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
form.instance.thread = self.thread
return super(ThreadView, self).form_valid(form)
I think you must add ForeginKey Feild into Views Feilds
fields = ['thread', 'post_title', 'author', 'post_text']
and be sure there is a data in thread model
Try adding this to your view:
def post(self, *args, **kwargs):
self.t_id = kwargs["t_id"]
return super(ThreadView, self).post(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.thread = Thread.objects.get(pk=self.t_id)
form.save_m2m()
return super(ModelFormMixin, self).form_valid(form)