Django: show external data in model based form - django

I have some external data (SOAP) that I want to show in a model-based-form.
The Model:
class UserProfile(User):
profile_email = models.EmailField()
company_name = models.CharField()
coc_number = models.CharField()
gender = models.CharField()
#etc
The Form:
class UserDetailsForm(forms.ModelForm):
class Meta:
model = UserProfile
The data is a dictionary:
u = {}
u['profile_email'] = 'monkey'
u['company_name'] = 'tiger'
u['coc_number'] = 'some number'
u['gender'] = 'M'
My question is: What is the best way to put the data into the form? What I have so far:
form = UserDetailsForm(initial=u)
This results in a form with all the data.
1) But is this the right way to fill a model-bases-form with external data?
2) How can I set the right value in a select option (Choose country for instance)?

Yes, this is appropriate way.
You need to set value for select/choices field in the dict similar to approach 1.
For example:
COUNTRY_CHOICES = (
('IN', 'India'),
('US', 'USA'),
)
....
#model field
country = models.CharField(choices=COUNTRY_CHOICES)
# then set it in dict as
u = {}
u['country'] = 'IN'
u['profile_email'] = 'monkey'
u['company_name'] = 'tiger'
u['coc_number'] = 'some number'
u['gender'] = 'M'
...

Related

django "can't adapt type '__proxy__'" error message

class GenderTypeEnum:
FEMALE = 1
MALE = 2
UNKNOWN = 3
types = (
(FEMALE, _("Female")),
(MALE, _("Male")),
(UNKNOWN, _("Unknown"))
)
class PersonModel(models.Model):
identity = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=75)
last_name = models.CharField(max_length=75)
gender = models.PositiveIntegerField(choices=GenderTypeEnum.types)
class StaffModel(models.Model):
person = models.ForeignKey('PersonModel', on_delete=models.CASCADE, related_name='staffs')
registration_number = models.CharField(max_length=50, unique=True)
start_date = models.DateField()
finish_date = models.DateField(null=True, blank=True)
I am using the following query to list the gender statistics of the staff
StaffModel.objects.values("person__gender").annotate(count=Count("person__gender"))
output:
[
{"person__gender":1, "count":1},
{"person_gender":2, "count":5}
]
But gender field is a choices field so that, output that I want like this:
[
{"person__gender":1, "gender_exp":"Male", "count":1},
{"person_gender":2, "gender_exp":"Female", "count":5}
]
I created the following class by looking at the answer given to #bachkoi32 Display name of a choice field in Django while using annotate
In order to output, I use this class:
class WithChoices(Case):
def __init__(self, model, field, condition=None, then=None, **lookups):
fields = field.split('__')
for f in fields:
model = model._meta.get_field(f)
if model.related_model:
model = model.related_model
choices = dict(model.flatchoices)
whens = [When(**{field: k, 'then': Value(v)}) for k, v in choices.items()]
return super().__init__(*whens, output_field=CharField())
I changed my query:
qs = StaffModel.objects.values("person__gender").annotate(gender_exp=WithChoices(StaffModel, 'person__gender'), count=Count("person__gender")).values("person__gender","gender_exp","count")
When I want to print the query result, it raise the error;
django.db.utils.ProgrammingError: can't adapt type 'proxy'
qs = StaffModel.objects.values("person__gender").annotate(gender_exp=WithChoices(StaffModel, 'person__gender'), count=Count("person__gender")).values("person__gender","gender_exp","count")
print(qs)
# raise error;
# django.db.utils.ProgrammingError: can't adapt type '__proxy__'
The labels for your choices are lazy translations, these can't be passed as values to a query, they need to be converted to strings using force_str
from django.utils.encoding import force_str
class WithChoices(Case):
def __init__(self, model, field, condition=None, then=None, **lookups):
fields = field.split('__')
for f in fields:
model = model._meta.get_field(f)
if model.related_model:
model = model.related_model
choices = dict(model.flatchoices)
whens = [When(**{field: k, 'then': Value(force_str(v))}) for k, v in choices.items()]
return super().__init__(*whens, output_field=CharField())

How to write a OR query when number of parameters may change?

I need to make a filter to show BookInstances with following conditions:
BookInstance.public = True
BookInstance.owner != current logged in user
BookInstance.book.title.icontains('some text')
BookInstance.book.description.icontains('some text')
BookInstance.book.authors(id_in=some list of ids)
BookInstance.book.categories(id_in=some list of ids)
The conditions will be combined as:
1 AND 2 AND ( ( 3 OR 4 ) OR 5 OR 6 )
3 & 4 use the same text for search.
current scaffolding in view:
searchedObjects = BookInstance.objects.filter(public=True)
if request.user.is_authenticated:
searchedObjects = searchedObjects.exclude(owner=request.user)
filterObj = dict(request.POST)
for key in filterObj:
if key == 'bookTitleOrDescription':
#condition 3 & 4
bookTitleOrDescription = filterObj[key][0]
elif key == 'author[]':
#condition 5
authorList = filterObj[key]
elif key == 'category[]':
#condition 6
categoryList = filterObj[key]
searchedObjects will have the query result.
The if, elif are required as some parameters may or may not be present. We must avoid writing 10 combinations for it.
models:
class Author(SafeDeleteModel):
name = models.CharField(max_length=255)
class Category(SafeDeleteModel):
title = models.CharField(max_length=255)
class Book(SafeDeleteModel):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
authors = models.ManyToManyField(Author)
categories = models.ManyToManyField(Category)
class BookInstance(SafeDeleteModel):
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
public = models.BooleanField(verbose_name='Will show in search ?')
lendable = models.BooleanField(verbose_name='In good condition ?')
You can filter your query for each field like this:
author = request.POST.get('author', None)
if author:
searchedObjects = searchedObjects.filter(author=author)
You can do it in following way:
filter = Q()
if key == 'category[]':
filter = filter | Q(book__category__id__in='some list of ids')
if key == 'author[]':
filter = filter | Q(book__authors__id__in='some list of ids')
....
....
data = BookInstance.objects.filter(filter)

Django foreign key is not set and hence unable to save form

I have a simple foreign key relationship between two tables. I am able to save the parent, but am unable to save the child which has a foreign key to the parent. This is what my models look like:
class Product(models.Model):
month_choices = tuple((m,m) for m in calendar.month_abbr[1:])
year_choices = tuple((str(n), str(n)) for n in range(2004, datetime.now().year +2 ))
id = models.AutoField(primary_key = True)
title = models.CharField(max_length = 1024)
product_type = models.ForeignKey(ProductType)
month = models.CharField(max_length =3, choices=month_choices)
year = models.CharField(choices=year_choices, max_length = 4)
project = models.CharField(max_length = 15, null = True, blank = True)
url = models.URLField(null = True, blank = True)
export_to_xsede = models.BooleanField()
#def __str__(self):
# return str(self.id)
class Meta:
db_table = "product"
class ProductResource(models.Model):
CHOICES = (('A','A'),('B','B'),('C','C'),('D','D'),('E','E'))
id = models.AutoField(primary_key = True)
product = models.ForeignKey(Product)
resource = models.CharField(choices=CHOICES, max_length = 15)
And my views:
class PublicationForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(attrs={'size':'70'}),required=False)
url = forms.CharField(widget=forms.TextInput(attrs={'size':'70'}),required=False)
class Meta:
model = Product
class ResourceForm(forms.ModelForm):
resource = forms.MultipleChoiceField(choices=ProductResource.CHOICES, widget = forms.CheckboxSelectMultiple)
class Meta:
model = ProductResource
I save the parent:
saved_publication = publications_form.save()
but am unable to save the resource form:
resource_form = ResourceForm(request.POST, instance = saved_publication)
resource_form.product = saved_publication
resource_form.save()
When I print resource_form.errors, I get:
<ul class="errorlist"><li>product<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
I have no idea why the foreign key is not getting set in this case.
I'm assuming you do not want to display the product field on the form, so you should exclude it from the form so the validation will pass:
class ResourceForm(forms.ModelForm):
resource = forms.MultipleChoiceField(choices=ProductResource.CHOICES, widget = forms.CheckboxSelectMultiple)
class Meta:
model = ProductResource
exclude = ['product']
Then in the view, just set the product manually after calling is_valid(). Just be sure to pass commit=False on the form.save() so that it will not actually save to the database until after you set the product. For example
...
saved_publication = publications_form.save()
resource_form = ResourceForm(request.POST)
if resource_form.is_valid():
resource = resource_form.save(commit=False)
resource.product = saved_publication
resource.save()

Cannot assign "u''": "Company.parent" must be a "Company" instance

I am getting this at every attempt.
Cannot assign "u''": "Company.parent" must be a "Company" instance.
I do not know what else to do.
The view code is still half baked, sorry for that.
Am I passing wrong parameters to the form?
I have the following model:
models.py
class Company(AL_Node):
parent = models.ForeignKey('self',
related_name='children_set',
null=True,
db_index=True)
node_order_by = ['id', 'company_name']
id = models.AutoField(primary_key=True)
company_name = models.CharField(max_length=100L, db_column='company_name') # Field name made lowercase.
next_billing_date = models.DateTimeField()
last_billing_date = models.DateTimeField(null=True)
weekly = 'we'
twice_a_month = '2m'
every_two_weeks = '2w'
monthly = 'mo'
billing_period_choices = (
(weekly, 'Weekly'),
(every_two_weeks, 'Every two weeks'),
(twice_a_month, 'Every two weeks'),
(monthly, 'Monthly'),
)
billing_period = models.CharField(max_length=2,
choices=billing_period_choices,
default=weekly)
objects = CompanyManager()
The following forms.py:
class newCompany(ModelForm):
company_name = forms.CharField(label='Company Name',
widget=forms.TextInput(attrs={'class': 'oversize expand input-text'}))
billing_period = forms.ModelChoiceField
next_billing_date = forms.CharField(widget=forms.TextInput(attrs={'class': 'input-text small', 'id': 'datepicker'}))
parent = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Company
fields = ["company_name", "parent", "billing_period", "next_billing_date"]
The following view:
def create_company(request):
userid = User.objects.get(username=request.user).id
my_company_id = CompanyUsers.objects.get(user_id=userid).company_id
my_company_name = Company.objects.get(id=my_company_id).company_name
machines = Title.objects.raw(
'select machines.id, title.name, machines.moneyin, machines.moneyout, moneyin - moneyout as profit, machines.lastmoneyinoutupdate, (select auth_user.username from auth_user where machines.operator = auth_user.id) as operator, (select auth_user.username from auth_user where machines.readers = auth_user.id) as readers from machines, title where machines.title = title.id and machines.company_id =%s',
[my_company_id])
if request.method == 'POST':
form_company = newCompany(request.POST)
if form_company.is_valid():
new_company = form_company.save(commit=False)
new_company.parent = my_company_id
if request.POST.get('select_machine'):
selected_machine = request.POST.getlist('select_machine')
percentage = request.POST.get('percentage')
if not Beneficiary.objects.check_assign_machine(my_company_id, selected_machine, percentage):
target_company_name = new_company.company_name
target_company_id = Company.objects.get(company_name=target_company_name).id
new_company.save()
Machines.objects.assign_machine(target_company_id, selected_machine)
Beneficiary.objects.create_beneficiary(percentage, target_company_name, my_company_id, selected_machine)
else:
invalid_machines = Beneficiary.objects.check_assign_machine(my_company_id, selected_machine, percentage)
return render(request, 'lhmes/createcompany.html',
{'form_company': form_company, 'machines': machines, 'my_company_name': my_company_name, 'invalid_machines' : invalid_machines})
else:
new_company.save()
else:
form_company = newCompany()
return render(request, 'lhmes/createcompany.html',
{'form_company': form_company, 'machines': machines, 'my_company_name': my_company_name})
The error message says you are trying to set a relationship with a string but Django expects the value to be an instance of the Company model. You should assign the foreign key fields with a real model instance instead of only the primary key.
I've spotted a few places in the code where you are assigning a PK:
new_company.parent = my_company_id
Where the model expects it to be an instance:
new_company.parent = Company.objects.get(id=my_company_id)
I really don't remember if this works, but you can try:
new_company.parent_id = int(my_company_id)
This would spare a trip to the database.

django accessing foreign key in model

i have the following models
class SchoolClass(models.Model):
id = models.AutoField(primary_key = True)
class_name = models.TextField()
level = models.IntegerField()
taught_by = models.ManyToManyField(User,related_name="teacher_teaching",through='TeachSubject')
attended_by = models.ManyToManyField(User,related_name='student_attending')
def __unicode__(self):
return self.class_name
class Meta:
db_table = 'classes'
class Relationship(models.Model):
rChoices = (
(1,'Mother'),
(2,'Father'),
(3,'Guardian'),
)
parent = models.ForeignKey(User,related_name='parent')
student = models.ForeignKey(User,related_name='child')
relationship = models.IntegerField(choices= rChoices)
#add in __unicode__ for admin name
class Meta:
unique_together = ('parent','student')
db_table = 'relationship
I have the the pk of the class, and I want to find out who are the parents of the students in the selected class.
My feeble attempt is:
selected_class = SchoolClass.objects.get(pk=class_id)
studs = selected_class.attended_by.all().select_related()
r = Relationship.objects.filter(student__in=students)
parents = [.parent for p in r]
Now, I am just curious if there is a shorter or more efficient way of doing this(i'm sure missed something in the docs) ?
This should work
parents = Relationship.objects.filter(student__schoolclass__id=class_id).values_list('parent', flat=True)
"To refer to a "reverse" relationship, just use the lowercase name of the model". (docs)