How to save MultipleChoiceField data using Django ModelForm - django

I'm trying to save data from a ModelForm that has MultipleChoiceFields. I want the user to be able to select multiple timeframes and have that data saved to the database.
So far, when submitting the form using the MultipleChoiceField, I get nothing returned.
Here's my models.py:
class InfoFormModel(models.Model):
YES_NO = (
('yes', 'Yes'),
('no', 'No'),
)
TIMEFRAME = (
('1_weeks', '1 Week'),
('2_weeks', '2 Weeks'),
('3_weeks', '3 Weeks'),
('4_weeks_plus', '4 Weeks+'),
)
PAGES_NEEDED = (
('about_page', 'About Page'),
('contact_page', 'Contact Page'),
('blog_page', 'Blog Page'),
('map_page', 'Map Page'),
('ecommerce_page', 'Ecommerce Page'),
)
brand_name = models.CharField(
blank=False, null=False, max_length=500, default='')
logo = models.CharField(choices=YES_NO, blank=False,
null=False, max_length=500, default='no')
what_is_the_service = models.TextField(
blank=False, null=False, max_length=5000, default='')
contact_number = models.BigIntegerField(blank=True, null=True, default='')
email = models.EmailField(blank=True, null=True,
max_length=300, default='')
timeframe = models.CharField(
choices=TIMEFRAME, max_length=100, blank=False, null=False, default='')
aim = models.TextField(blank=False, null=False,
max_length=5000, default='')
products_product_images = models.CharField(
choices=YES_NO, blank=False, max_length=500, null=False, default='')
products_info = models.CharField(
choices=YES_NO, blank=False, null=False, max_length=500, default='')
pages_needed = models.CharField(
choices=PAGES_NEEDED, blank=True, null=True, max_length=500, default='')
def __str__(self):
return self.brand_name
forms.py:
class InfoForm(forms.ModelForm):
YES_NO = (
('yes', 'Yes'),
('no', 'No'),
)
TIMEFRAME = (
('1_weeks', '1 Week'),
('2_weeks', '2 Weeks'),
('3_weeks', '3 Weeks'),
('4_weeks_plus', '4 Weeks+'),
)
PAGES_NEEDED = (
('about_page', 'About Page'),
('contact_page', 'Contact Page'),
('blog_page', 'Blog Page'),
('map_page', 'Map Page'),
('ecommerce_page', 'Ecommerce Page'),
)
brand_name = forms.CharField(label='', widget=forms.TextInput(attrs={'placeholder' : 'Business/Brand Name?'}))
logo = forms.CharField(label='Do you have a logo?', widget=forms.RadioSelect(choices=YES_NO))
what_is_the_service = forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder' : 'What service are you providing?'}))
contact_number = forms.CharField(label='', widget=forms.NumberInput(attrs={'placeholder' : 'Contact number'}))
email = forms.CharField(label='', widget=forms.EmailInput(attrs={'placeholder' : 'Email address'}))
timeframe = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,
choices=TIMEFRAME)
aim = forms.CharField(label='', widget=forms.Textarea(attrs={'placeholder' : 'What will be the aim for your website?'}))
products_product_images = forms.CharField(label='Do you have any product images?', widget=forms.RadioSelect(choices=YES_NO))
products_info = forms.CharField(label='Do you your product info (eg, product names, pricing, descriptions etc.)?', widget=forms.RadioSelect(choices=YES_NO))
pages_needed = forms.CharField(label="Select which pages you'll need?", widget=forms.RadioSelect(choices=PAGES_NEEDED))
class Meta:
model = InfoFormModel
fields = (
'brand_name',
'logo',
'what_is_the_service',
'contact_number',
'email',
'timeframe',
'aim',
'products_product_images',
'products_info',
'pages_needed',
)
views.py
def home(request):
submitted = False
if request.method == 'POST':
form = InfoForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'thanks.html')
# return HttpResponseRedirect('/?submitted=True')
else:
form = InfoForm()
if 'submitted' in request.GET:
submitted = True
form = InfoForm
context = {
'form': form,
'submitted': submitted,
}
return render(request, 'home.html', context)
Any help would be greatly appreciated. Thanks!

I think that there is a discrepancy between your model fields and the form fields.
In your model, you have defined the timeframe field as:
timeframe = models.CharField(choices=TIMEFRAME, max_length=100, blank=False, null=False, default='')
And in your form code, you have defined the timeframe field as:
timeframe = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=TIMEFRAME)
After validating the data, MultipleChoiceField will try to assign a list to the timeframe attribute on the InfoFormModel (btw, terrible naming). This model attribute should contain a string.
I think there are two ways to resolve this.
Use posgtres' ArrayField in the model. The ArrayField supports assigning lists to it. Don't forget to make migrations and run them. Also, note that this only works if you're using a PostgreSQL DB.
from django.contrib.postgres.fields import ArrayField
class InfoFormModel(models.Model):
...
timeframe = ArrayField(CharField(choices=TIMEFRAME, max_length=100), default=[])
...
Override MultipleChoiceField to return a string instead of a list. By overriding, you can make the field to return the list as a comma-separated string instead. This would also mean that you need to remove the choices kwarg from the field in the model code since the fields will not pass validation in case of more than one option being saved.

Related

how to create/add model object to a database model of a selected type of user in the registration form

how to create/add model object to a database model of a selected type of user in the registration form? what I want is if the selected type of user is a student then I want to automatically create it in the student model after being registered not just in the User model. how to achieve that?
models.py
class User(AbstractUser):
is_admin = models.BooleanField('Is admin', default=False)
is_teacher = models.BooleanField('Is teacher', default=False)
is_student = models.BooleanField('Is student', default=False)
class Student(models.Model):
GENDER = (
('1', 'Male'),
('2', 'Female')
)
STATUS = (
('1', 'Ongoing'),
('2', 'On Probition'),
('3', 'NA')
)
name = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
id_number = models.IntegerField()
gender = models.CharField(
max_length=10, choices=GENDER, blank=True, null=True)
date_of_birth = models.CharField(max_length=20, blank=True, null=True)
course = models.ForeignKey(
Course, on_delete=models.CASCADE, blank=True, null=True)
year_level = models.IntegerField()
status = models.CharField(max_length=10, choices=STATUS, default='3')
def __str__(self):
return str(self.name)
class Teacher(models.Model):
name = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
def __str__(self):
return str(self.name)
forms.py
class SignUpForm(UserCreationForm):
username = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control"
}
)
)
password1 = forms.CharField(
widget=forms.PasswordInput(
attrs={
"class": "form-control"
}
)
)
password2 = forms.CharField(
widget=forms.PasswordInput(
attrs={
"class": "form-control"
}
)
)
email = forms.CharField(
widget=forms.TextInput(
attrs={
"class": "form-control"
}
)
)
class Meta:
model = User
# model = Profile
fields = ('username', 'email', 'password1', 'password2',
'is_admin', 'is_teacher', 'is_student')
I think the logic should be added here in views.py but I don't know how to do it.
views.py
def register(request):
msg = None
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
msg = 'user created'
return redirect('login_view')
else:
msg = 'form is not valid'
else:
form = SignUpForm()
return render(request, 'register/register.html', {'form': form, 'msg': msg})

Django CreateView - created_by, modified_by mixin not working

I have followed spapas tutorial on CBV's and tried to apply a mixin on a create view. But looks like it doesn't evaluate correctly the if not form.invoice.requester for a user foreign key because it always says: RelatedObjectDoesNotExist and it points to the field evaluated in the if not line.
What can be wrong?
views.py
class AuditableMixin(object, ):
def form_valid(self, form, ):
if not form.instance.requester:
form.instance.requester = self.request.user
form.instance.modified_by = self.request.user
return super().form_valid(form)
class NewOrderView(LoginRequiredMixin, PermissionRequiredMixin, AuditableMixin, generic.CreateView):
permission_required = 'orders.add_order'
form_class = NewOrderForm
model = Order
title = 'New Order'
extra_context = {'title': title}
forms.py
class NewOrderForm(forms.ModelForm):
class Meta:
model = Order
widgets = {
'order_details': forms.Textarea,
}
exclude = (
'status',
'invoice',
'requester',
'modified_by',
)
models.py
class Order(models.Model):
STATUS_CHOICES = (
('open', 'Open'),
('inprogress', 'In Progress'),
('rejected', 'Rejected'),
('closed','Closed'),
('resolved','Resolved'),
)
subject = models.CharField(
max_length=30,
)
requester = models.ForeignKey(
User,
on_delete=models.PROTECT,
related_name='orders',
)
order_details = models.TextField(
)
created = models.DateTimeField(
auto_now_add=True,
)
updated = models.DateTimeField(
auto_now=True,
)
status = models.CharField(
max_length=12,
default='open',
choices= STATUS_CHOICES,
)
invoice = models.ForeignKey(
Invoice,
on_delete=models.PROTECT,
blank= True,
null=True,
related_name='orders',
)
modified_by = models.ForeignKey(
User,
on_delete=models.PROTECT,
related_name='orders_modified',
)
def __str__(self):
return self.subject
def get_absolute_url(self):
return reverse('Order_Detail', args=[str(self.pk)])
class Meta:
ordering = ['-created']
Thank you.
When calling the ForeignKey object on a model, you're actually telling Django to perform the query to fetch the related object. To check if the relationship exists, you should check <fieldname>_id which is the actual name of the field:
if not form.instance.requester_id:
form.instance.requester = self.request.user
That way if requester_id is None you don't perform the query, which would through the RelatedObjectDoesNotExist error.

Django-Tables2 LinkColumn link goes to wrong item

I have a Django project and I am having issues with the hyperlink (LinkColumn from Django-tables2) going to the incorrect entry and I cannot figure out why it is occurring or how to fix it.
Very specifically, I can go to the admin view and create a publication. When it comes to setting the author (a.k.a. pi) or sample, there is a drop down menu for foreign key fields (sample/pi) that shows all existing entries from which I can choose one. When I choose a sample and pi then look at the table rendering, the hyperlink is there for the sample, pi, and the publication title. The publication title correctly takes me to the publication_detail page. The hyperlink for the sample will take me to a sample detail page, but it is not the same sample I selected from the admin page. I have the same issue for the author; it takes me to the detail view page of AN author, just not the one I selected from the admin page.
I use django-tables2 several times throughout the project and like how the tables are rendered, but cannot figure out how to address this problem. I have included some of my code (please note I included a bit of the PI and Sample model, but not all of it).
Any assistance is greatly appreciated.
models.py
class PI(models.Model): #this is a smattering of the PI model
l_name = models.CharField('L Name', blank=False, max_length=100, default='')
f_name = models.CharField('F Name', blank=False, max_length=100, default='')
m_name = models.CharField('MI', null=True, blank=True, max_length=1, default='' )
phone = PhoneField(blank=True, default='')
email = models.EmailField('Email', blank=True, max_length=100, default='')
class Sample(models.Model): #this is a smattering of the Sample model
sample_name = models.CharField('Sample', max_length=16)
pi = models.ForeignKey(PI, on_delete=models.SET_NULL, null=True)
submitter = models.ForeignKey('Submitter', blank=True, on_delete=models.SET_NULL, null=True)
class Publication(models.Model):
sample = models.ForeignKey(Sample, on_delete=models.SET_NULL, null=True)
author = models.ForeignKey(PI, on_delete=models.SET_NULL, null=True)
title_p = models.CharField('Title', max_length=200, blank=False, default='')
volume = models.IntegerField('Volume', blank=True, null=True)
number = models.IntegerField('Number', blank=True, null=True)
pages = models.CharField('Pages', default='', max_length=20, blank=True)
year = models.IntegerField('Year', blank=True, null=True)
doi = models.CharField('DOI', default='', max_length=30, blank=False)
journal = models.CharField('Journal', default='', max_length=100, blank=False)
abstract = models.CharField('Abstract', default='', max_length=1000, blank=False)
issn = models.CharField('ISSN', default='', max_length=10, blank=False)
url = models.CharField('URL', default='', max_length=100, blank=False)
eprint = models.CharField('Eprint', default='', max_length=100, blank=False)
class Meta:
ordering = ('sample', 'author', 'title_p', 'journal', 'volume', 'number', 'pages', 'year', 'doi', 'abstract', 'issn', 'url', 'eprint')
def get_absolute_url(self):
return reverse('publication-detail', args=[str(self.id)])
def __str__(self):
return f'{self.sample}, {self.author}, {self.title_p}, {self.volume}, {self.number}, {self.pages}, {self.year}, {self.doi}, {self.journal}, {self.abstract}, {self.issn}, {self.url}, {self.eprint}'
tables.py
class PublicationTable(tables.Table):
sample = tables.LinkColumn('sample-detail', args=[A('pk')])
author = tables.LinkColumn('pi-detail', args=[A('pk')])
title_p = tables.LinkColumn('publication-detail', args=[A('pk')])
class Meta:
model = Publication
fields = ( 'sample', 'author', 'title_p', 'journal', 'year', )
exclude = ( 'volume', 'number', 'pages', 'doi', 'abstract', 'issn', 'url', 'eprint', )
list_display = ('sample', 'author', 'title_p', 'year', 'journal', )
views.py
class PublicationListView(generic.ListView):
model = Publication
paginate_by = 100
#login_required
def publication_view(request, pk):
publication = Publication.objects.get(pk = pk)
table = PublicationTable(Publication.objects.filter(publication=pk))
RequestConfig(request).configure(table)
return render(request, 'samples/publication_detail.html', {'publication': publication, 'publication-detail': table})
#login_required
def publication_table(request):
table = PublicationTable(Publication.objects.all())
RequestConfig(request).configure(table)
return render(request, 'samples/publication_list.html', {'publication_table': table})
class PublicationDetailView(generic.DetailView):
model = Publication
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('samples/', views.sam, name='sam'),
path('sample/<int:pk>', views.SampleDetailView.as_view(), name='sample-detail'),
path('pi/', views.pi_table, name='pi_table'),
path('pi/<int:pk>', views.pi_view, name='pi-detail'),
path('publication/', views.publication_table, name='publication_table'),
path('publication/<int:pk>', views.PublicationDetailView.as_view(), name='publication-detail'),
]
A bit of code from samples/templates/samples/publication_list.py
{% render_table publication_table %}
Well, passing pk through accessor means, it will pass the primary key of Publication Model objects for pi-details, sample-details, and so on. So you need to change it so that you pass the respective primary keys throught the accessor like this:
class PublicationTable(tables.Table):
sample = tables.LinkColumn('sample-detail', args=[A('sample_id')])
author = tables.LinkColumn('pi-detail', args=[A('author_id')])
title_p = tables.LinkColumn('publication-detail', args=[A('pk')])

Django CMS: How can I show from database in setting window

I want to show data in dropdown from database in setting window. Right now I am showing from hard-coded array.
MY_CHOICES = (
('a', 'Cat1'),
('b', 'Cat2'),
)
categories = models.CharField("Survey", help_text="Select Survey", choices=MY_CHOICES, max_length=3, blank=True)
models.py
# encoding: utf-8
from cms.models import CMSPlugin, python_2_unicode_compatible
from django.db import models
from django.core.exceptions import ValidationError
from cms.models import CMSPlugin
class Survey(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
def __unicode__(self):
return (self.name)
def questions(self):
if self.pk:
return Question.objects.filter(survey=self.pk)
else:
return None
class SurveyPluginModel(CMSPlugin):
MY_CHOICES = (
('a', 'Cat1'),
('b', 'Cat2'),
)
categories = models.CharField("Survey", help_text="Select Survey", choices=MY_CHOICES, max_length=3, blank=True)
name = models.CharField("Survey Name", max_length=255, default='Survey Name',
help_text='Enter Survey Name')
description = models.CharField("Survey Description", max_length=500, blank=True, help_text='Write Description here')
def __str__(self):
return "Returning some Survey Text"
I want to show survey in Edit Setting Window.
How to fill surveys from db value?
Try this
class Survey(models.Model):
name = models.CharField(max_length=400)
description = models.TextField()
def __unicode__(self):
return (self.name)
def questions(self):
if self.pk:
return Question.objects.filter(survey=self.pk)
else:
return None
class SurveyPluginModel(CMSPlugin):
categories = models.ForeignKey("Survey", help_text="Select Survey", max_length=3, blank=True)
name = models.CharField("Survey Name", max_length=255, default='Survey Name',
help_text='Enter Survey Name')
description = models.CharField("Survey Description", max_length=500, blank=True, help_text='Write Description here')
def __str__(self):
return "Returning some Survey Text"

ModelForm's drop-down box for Foreign Key not in alphabetical order

I have created a foreign key field in the "ReviewComments" model and have included the command "order_with_respect_to". However, the drop-down box that appears in the webpage does not sort the service providers by alphabetical order. Is there something that the code is missing?
Models.py
class ServiceProvider(models.Model):
identification_number = models.AutoField(primary_key=True, )
license_number = models.CharField(max_length=10, null=True, blank=True, )
individual_name = models.CharField(max_length=60, )
corporate_name = models.CharField(max_length=120, )
reg_address = models.CharField(max_length=180, )
email_address = models.EmailField(max_length=254, null=True, blank=True, )
land_line = models.CharField(max_length=50, null=True, )
hand_phone_line = models.CharField(max_length=50, null=True, blank=True, )
service_type = models.CharField(max_length=20)
def __str__(self):
return "%s (%s)" % (self.individual_name, self.service_type)
class ReviewComments(models.Model):
comment_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=120)
comment = models.CharField(max_length=250)
receipt_document = models.FileField(upload_to='receipt_document', default=None, )
service_provider = models.ForeignKey(ServiceProvider, on_delete=models.CASCADE)
user_id = models.CharField(max_length=10, null=True, blank=True, )
class Meta:
order_with_respect_to = 'service_provider'
def __str__(self):
return self.title + "\n" + self.comment
Forms.py
class ReviewForm(ModelForm):
class Meta:
model = ReviewComments
fields = ['title', 'comment', 'service_provider', 'receipt_document', ]
widgets = {
'title': Textarea(attrs={'cols': 80, 'rows': 1}),
'comment': Textarea(attrs={'cols': 80, 'rows': 10}),
}
order_with_respect_to is not what you are looking for. That will order your ReviewComments with respect to their corresponding ServiceProvider - it does not alter the order of the ServiceProvider.
If you want your service providers to be listed alphabetically then you need to set the default ordering on the the ServiceProvider model, specifying which field you want to order by:
class ServiceProvider(models.Model):
# ...
class Meta:
ordering = ['individual_name']