I have a model form that have multiple choice fields. using AJAX to update form choic fields upon changed field.
Model:
class Student(models.Model):
CLASSROOM = 0
GROUPROOM = 1
HOMEROOM = 3
STUDENT_RECORD_TYPES = [
(CLASSROOM,_("Classroom")),
(GROUPROOM,_("Group")),
(HOMEROOM,_("Home Edu")),
]
school = models.ForeignKey(School,on_delete=models.CASCADE,blank=False,related_name='student_records')
grade = models.ForeignKey(Grade,on_delete=models.CASCADE,blank=False,related_name="student_records")
record_type = models.PositiveSmallIntegerField(_("Record Type"),choices=STUDENT_RECORD_TYPES,default=0)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['school','grade', 'record_type'],
name='unique_school_grade_record'
),
]
def __str__(self):
return "Record ID: {}".format(self.pk)
Views.py:
def update_students(request,pk):
updated_table=None
student_record = get_object_or_404(Student,pk=pk)
if request.POST:
form = StudentForm(request.POST or None,instance=student_record)
if form.is_valid():
form.save()
messages.success(request,_("Student record Updated Successfully!"))
#Getting data for view
updated_table = update_students_table(request)
else:
messages.error(request,_("Invalid Input, Please check!"))
else:
form = StudentForm(request.GET or None,instance=student_record)
context = {}
# load form template
context['form'] = form
form_template_path = "components/forms/student_update.html"
html_form = loader.render_to_string(form_template_path, context, request)
context['form'] = html_form
return JsonResponse(context)
Forms.py:
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
self.fields['school'].widget.attrs['class'] += ' select2'
#the issue stands here
#self.data.get('school') returns none even if its exist in form data
if self.data.get('school'):
self.fields['grade'].queryset = Grade.objects.filter(school=self.data.get('school'))
else:
self.fields['grade'].queryset = Grade.objects.none()
class Meta:
model = Student
fields = '__all__'
the strange behavior drives me crazy because when I reselect the school it updates the grade choices normally (with no option selected!), but when I open edit instance form the data is there but grade field have no options in it!
Related
I'm currently working on a Django app that will parse the contents of an uploaded log file to the associated database in my Django project. I've managed to get it all running as expected except it won't associate my uploaded data with the model's ForeignKey. I can assign null=True which resolves the integrity error but then of course, it doesn't assign any of the uploaded data to that ForeignKey. Here's the code:
models.py
class Case(models.Model):
case_ref = models.CharField(max_length=8)
oic = models.CharField(max_length=50)
subject = models.CharField(max_length=100)
submitted_date = models.DateTimeField(default=datetime.now, blank=True)
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return self.case_ref + " " + self.subject
class TeamviewerLogs(models.Model):
case = models.ForeignKey(Case, on_delete=models.DO_NOTHING)
teamviewer_id = models.IntegerField()
teamviewer_name = models.TextField()
connection_start = models.TextField()
connection_end = models.TextField()
local_user = models.TextField()
connection_type = models.TextField()
unique_id = models.TextField()
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return str(self.teamviewer_id) + " - " + str(self.teamviewer_id)
forms.py
class UploadLog(forms.ModelForm):
file = forms.FileField()
class Meta:
model = TeamviewerLogs
fields = [
'file'
]
views.py
def add_logs(request, pk):
case = get_object_or_404(Case, pk=pk)
if request.method == 'POST':
form = UploadLog(request.POST, request.FILES)
if form.is_valid():
teamviewer = form.save(commit=False)
teamviewer.case = case
log_file = request.FILES['file']
log_file = filter(None, (line.rstrip() for line in log_file))
for lines in log_file:
split = lines.decode('utf-8').split('\t')
teamviewer_id = split[0]
teamviewer_name = split[1]
connection_start = split[2]
connection_end = split[3]
local_user = split[4]
connection_type = split[5]
unique_id = split[6]
teamviewer = TeamviewerLogs(teamviewer_id=teamviewer_id, teamviewer_name=teamviewer_name,
connection_start=connection_start, connection_end=connection_end,
local_user=local_user, connection_type=connection_type, unique_id=unique_id)
teamviewer.save()
return redirect('tv_log_details', pk=case.pk)
form.save()
else:
form = UploadLog()
return render(request, 'teamviewer/add_logs.html', {'form': form})
But when I click to upload the file I'm hit with:
When it tries to execute teamviewer.save().
I've been trying to resolve this issue for hours and have tried so many different variations of answers from Stackoverflow or previous code I've used that has worked for different models but I've hit a brick wall...hard!
Any help anyone can offer would be greatly appreciated.
Ok, so here's an example of the concept I've suggested in the comments.
I've got a view which passes some data to the a form;
class ListingDetailView(DetailView):
""" Listing detail page """
model = Listing
template_name = 'listing.html'
def get_form_kwargs(self):
"""Return the kwargs for the form"""
kwargs = {}
initial = {
'listing': self.object,
}
kwargs['initial'] = initial
return kwargs
def get_form(self):
form = ApplicationSignupForm(
**self.get_form_kwargs()
)
return form
def get_context_data(self, **kwargs):
""" Add our form to the context """
context = super().get_context_data(**kwargs)
context['form'] = self.get_form()
return context
The form then makes use of that initial data and sets the field it relates to as hidden. I don't validate this data, but I'll try to show how you might do that;
class ApplicationSignupForm(forms.ModelForm):
class Meta:
""" Setup the form """
fields = (
'listing',
...
)
model = Application
widgets = {
'listing': forms.HiddenInput()
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
initial_data = kwargs['initial']
self.listing = initial_data.get('listing')
def clean(self):
"""
Custom form cleaning
"""
cleaned_data = super().clean()
listing = cleaned_data.get('listing')
if listing != self.listing:
self.add_error('listing', "You can't modify this value")
return cleaned_data
I am trying to pass logged in user to form that i would like to save.
forms.py
class SpotForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SpotForm, self).__init__(*args, **kwargs)
self.fields['gross_weight'].widget = forms.NumberInput(attrs={'min':0})
self.fields['volume'].widget = forms.NumberInput(attrs={'min': 0})
class Meta:
model = Spot
fields = [
'gross_weight','volume','origin_country','origin_port',
'dest_country','dest_port','ship_week','requestor'
]
models.py
class Stakeholder(models.Model):
user = models.OneToOneField(User,null=True,blank=True,on_delete=models.CASCADE)
company_name = models.CharField(max_length=15)
mail = models.CharField(max_length=40)
def __str__(self):
return self.mail
class Spot(models.Model):
STATUSES = (
('Open','Open'),
('Closed','Closed')
)
gross_weight = models.FloatField(null=False,default=0,validators=[MinValueValidator(0)])
volume = models.FloatField(null=False,default=0,validators=[MinValueValidator(0)])
origin_country = models.CharField(
validators=[RegexValidator(regex='[A-Z]{2}', message='Country code is two letters')], max_length=2,null=True)
origin_port = models.CharField(
validators=[RegexValidator(regex='[A-Z]{3}', message='Port code is three letters')], max_length=3,null=True)
dest_country = models.CharField(
validators=[RegexValidator(regex='[A-Z]{2}', message='Country code is two letters')], max_length=2,null=True)
dest_port = models.CharField(
validators=[RegexValidator(regex='[A-Z]{3}', message='Port code is three letters')], max_length=3,null=True)
time_registered = models.DateField(default=timezone.now)
spot_status = models.CharField(max_length=6,default='Open', choices=STATUSES)
ship_week = models.CharField(max_length=2,null=True)
requestor = models.ForeignKey(Stakeholder,null = True,on_delete=models.CASCADE)
def __str__(self):
return self.origin_country + self.origin_port + '-' + self.dest_country +self.dest_port + '-' + self.ship_week
views.py
def register_spot(request):
my_user = Stakeholder.objects.get(user=request.user)
form = SpotForm()
if request.method =='POST':
print("print",request.POST)
form = SpotForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
print(form.errors)
context = {'form': form}
return render(request, 'spotrequesting/register_spot.html', context)
When i submit the form i am getting an error in command prompt stating "This field is required" for "requestor". After that - dropdown list for this field come up on screen and i can select out of two registered users i have. But even selecting something from this list and again submitting the form is giving me the same error.
Checking "my_user" variable - it is showing me that i am logged in.
Is there a way to pass to "requestor" field currently logged in user?
I was able to get the form saved only by deleting "requestor" from "fields" in SpotForm (which gave me "None" in the end for this field in database) but that's not the desired outcome.
Any suggestion would be highly appreciated.
You are not really passing the stakeholder instance to the requestor field in the form are you? So you will have to do:
form = SpotForm(requestor = my_user)
I have two models and forms linked by the ForeignKey 'squad'. In my templates I have the user first typing the Squad name and then the shooters. I am trying to hardcode the 'squad' field of my Shooters with the 'squad_name' of my ShooterSquad so the user doesn't have to type the squad name every time for every shooter.
models.py
class ShooterSquad(models.Model):
squad_name = models.CharField(unique=True, max_length=100)
school = models.CharField(max_length=100, null=False)
def __str__(self):
return self.squad_name
class Shooter(models.Model):
name = models.CharField(max_length=100)
squad = models.ForeignKey(ShooterSquad, to_field='squad_name', related_name='squad', on_delete=models.PROTECT)
def __str__(self):
return self.name
forms.py
class ShooterSquadForm(forms.ModelForm):
class Meta:
model = ShooterSquad
fields = ['squad_name', 'squad_total_score', ]
class ShooterForm(forms.ModelForm):
class Meta:
model = Shooter
fields = '__all__'
class BaseShooterFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseShooterFormSet, self).__init__(*args, **kwargs)
self.queryset = Shooter.objects.none()
ShooterFormSet = inlineformset_factory(
ShooterSquad, Shooter,
form=ShooterForm,
formset=BaseShooterFormSet,
extra=1,
max_num=3,
exclude=('squad',)
)
views.py
def add_multiple_shooters(request):
if request.method == 'POST':
squad_form = ShooterSquadForm(request.POST)
formset = ShooterFormSet(request.POST)
if squad_form.is_valid() and formset.is_valid():
set_squad = squad_form.cleaned_data.get('squad_name')
for f in formset.forms:
f.cleaned_data['squad_id'] = set_squad
f.cleaned_data['squad'] = set_squad
print(formset.cleaned_data)
squad_form.save()
formset.save()
return redirect('anasp:mainpage')
else:
print("ERROR")
formset = ShooterFormSet()
squad_form = ShooterSquadForm()
context = {
"title": title,
"formset": formset,
"squad_form": squad_form,
}
return render(request, "anasp/scores/shooter_formset.html", context)
Input Form Sample
My cleaned_data prints: [{'shooter_number': 67, 'squad': 'Woodland', 'name': 'Legolas', 'DELETE': False, 'id': None, 'shooter_score': 39, 'squad_id': 'Woodland'}]
So it seems that the squad_id has changed right? Wrong. When I look in my db my squad_id is <null>
How do I fix that?
Python: 3.5.4 Django: 1.8
I fixed it by not committing the save before all the changes were made:
if squad_form.is_valid():
squad = squad_form.save(commit=False)
if formset.is_valid():
shooters_to_save = list()
for f in formset.forms:
shooter = f.save(commit=False)
shooter.squad = squad
shooters_to_save.append(shooter)
squad.save()
for shooter in shooters_to_save:
shooter.save()
return redirect('anasp:mainpage')
I'm trying to modify an existing row of my database, it's a from an intermediary table named "CharacterSkill" with an unique together constraint :
models.py
class CharacterSkill(models.Model):
character = models.ForeignKey(Character, on_delete=models.CASCADE)
level = models.IntegerField(default=0)
skill = models.ForeignKey(Skill, on_delete=models.CASCADE)
class Meta:
unique_together = ("character","skill")
I did a form to change the level but I'm unable to save the form, I have two errors messages from both fields "character" & "skill":
Select a valid choice. That choice is not one of the available choices.
Can you help on that please ? :)
My form :
class SkillCreateForm(forms.ModelForm):
class Meta:
model = CharacterSkill
fields = ('skill','level','character',)
my view :
def skill_update(request,skillpk,instancepk):
form = SkillCreateForm(request.POST)
user = User.objects.get (id = request.user.id)
instance = Character.objects.get (id = instancepk)
skill = CharacterSkill.objects.get(id = skillpk)
data = {'character' : instance,
'skill' : skill.skill,
'level' : skill.level,
}
if form.is_valid():
form.save()
return redirect('persomaker:skill_list', instance.id)
else:
form = SkillCreateForm(data)
#form.fields['skill'].widget = HiddenInput()
#form.fields['character'].widget = HiddenInput()
return render(request, 'character/create_skill.html',
{'instance':instance,
'skill':skill,
'form': form,})
based on a book, I changed the way I bounded the form to use instance :
def skill_update(request,skillpk,instancepk):
user = User.objects.get (id = request.user.id)
instance = Character.objects.get (id = instancepk)
skill = CharacterSkill.objects.get(id = skillpk)
if request.method == "POST":
form = SkillModifyForm(request.POST,instance = skill,)
print (skill)
if form.is_valid():
form.save()
return redirect('persomaker:skill_list', instance.id)
else:
form = SkillModifyForm(instance = skill,)
return render(request, 'character/create_skill.html',
{'instance':instance,
'skill':skill,
'form': form,})
i want to send sms to selected user form multiple choices by admin side but, I am unable to get the values from the select_users field after the selection is done by the admin. Please help me to solve this.
here's my code
forms.py
class SendSMSForm(forms.ModelForm):
select_users = forms.ModelMultipleChoiceField(queryset=SignUp.objects.all(), widget=forms.CheckboxSelectMultiple)
class Meta:
model = SendSMS
fields = ['sms_subject','sms_text']
widgets = {
'sms_text': forms.Textarea()
}
def __init__(self, *args, **kwargs):
super(SendSMSForm, self).__init__(*args, **kwargs)
self.fields['select_users'].choices = [(str(item.mobile_no), str(item.mobile_no)) for item in SignUp.objects.all()]
admin.py
class SendSMSAdmin(admin.ModelAdmin):
change_form_template = "admin/sms/SmsMarketing/change_form.html"
form = SendSMSForm
list_display = ['sms_subject','sms_text','date1']
list_filter = ('sms_subject',)
def get_form(self, request, obj=None, **kwargs):
if request.method == 'POST':
form = SendSMSForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['sms_subject']
message = form.cleaned_data['sms_text']
***users = form.cleaned_data['select_users']***
print users
obj = SendSMS()
for user in users:
result = obj.sendsms(message, user)
obj_name = User.objects.get(username=user)
if result:
SmsHistory.objects.create(
number=user,
recipient=obj_name.get_full_name(),
sms_subject=subject,
sms_text=message,
sms_type = "Promotional SMS"
)
else:
return SendSMSForm
return SendSMSForm
You could try:
select_users = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple)