Django function as Charfield Choices - django

Consider the following models with the following fields:
Powers:
class Power(models.Model):
...
half = models.BooleanField()
micro = models.BooleanField()
UseP:
class UseP(models.Model):
...
power_policy = models.ForeignKey(Power, on_delete=models.CASCADE)
def use_types():
TYPES = []
TYPES.append(("F", "Full Use"))
if power_policy.half:
TYPES.append(("H", "Half Use"))
if power_policy.micro:
TYPES.append(("M", "Micro Use"))
return tuple(TYPES)
use_type = models.CharField(max_length=1, choices=use_types())
The function doesn't run. As you can see, when I try without the "self" arguments, it says that power_policy is not defined. If I do it like self.power_policy, it recognizes the power policy but then when I got and call the function like use_type = models.CharField(max_length=1, choices=self.use_types()), it says that the self keyword is not defined in this line.
I think the code explains it all, but in case it doesn't I want to provide choices to my user after they choose the Power, according to the power option. For which, I created a function but it doesn't really seem to work. What am I doing wrong or is it not even possible like this?
Thank You for your time,

If you create a method in your class, which uses fields from that class - you have to pass self argument to your method.
Change your method to: def use_types(self): then you can use your fields like self.power_policy.
You cannot use your field use_type like this. If you want to set use_type based on power_policy field you can do this in your save method like so:
def save(*args, **kwargs):
if self.power_policy and self.power_policy.half:
self.use_type = 'H'
elif self.power_policy and self.power_policy.micro:
self.use_type = 'M'
return super(UseP, self).save(*args, **kwargs)

Related

How to fill value in a limit_choices_to option in OneToOneField for Django Admin?

I need to limit the choices in a Django Admin form but cannot come up with a solution. Given these models:
class ProductFamily(models.Model):
name = models.CharField(max_length=250)
default_product_to_display = models.OneToOneField('Product', limit_choices_to={'product_family': ??????})
# Enclosing Product reference in quotes is necessary since the Product declaration is below ProductFamily declaration
class Product(models.Model):
name = models.CharField(max_length=250)
image = models.ImageField(upload_to='images/',blank=True,null=True)
product_family = models.ForeignKey(ProductFamily)
What should be placed in the ??????'s in class ProductFamily? I've tried everything I can think of including name, 'name' ProductFamily, 'Product Family', self, self.id. The options with self return an Internal Server Error. The quoted options return: 'invalid literal for int()'. When "name" is used unquoted, it returns:
int() argument must be a string or a number, not 'CharField'.
If I put a hard-coded integer value in to test it, then it works fine, so it is obviously looking for an integer value.
I also tried, without success, placing both
id = models.AutoField(primary_key=True)
in the declaration with id at the ??????'s as well as defining a function that returns self.id, but none of this works.
So - how do I limit the selection of products by showing only those whose product_family id's match those of the current ProductFamily?
The best way to do this is to use a custom form to limit the queryset of the field, as trying to set limit_choices_to dynamically is super tough, if not completely impractical.
Example:
class ProductFamilyForm(forms.ModelForm):
def __init__(self *args, **kwargs):
if 'initial' in kwargs:
self.fields['default_product_to_display'].queryset = Product.objects.filter(
product_family=initial.product_family)

autocomplete_light is not showing correct suggestions with choice_for_request

I have autocomplete-light in the django modal form. I wanted to apply dynamic filtering in the suggestion box, that's why I have used choice_for_request() in the autocompletebasemodel. But because of using choice_for_request(), the suggestions are not according to the keyword typed but all the values that can be entered.
This is my form:
class CamenuForm(autocomplete_light.ModelForm):
class Meta:
model = Ca_dispensaries_item
exclude = ('dispensary',)
autocomplete_fields = ('item',)
def __init__(self, *args, **kwargs):
self.category = kwargs.pop('category', None)
super(CamenuForm, self).__init__(*args, **kwargs)
self.fields['item'].queryset=Items.objects.filter(product_type__name=self.category)
This is the registry and the class:
autocomplete_light.register(Items, AutoComplete )
class:
class AutoComplete(autocomplete_light.AutocompleteModelBase):
search_fields=('item_name',)
def choices_for_request(self):
category = self.request.session.get('category','')
if category:
choices = Items.objects.filter(product_type__name=category)
return self.order_choices(choices)[0:self.limit_choices]
I really dont know what changes to make in changes_for_request so as to make it work correctly
After going through various documents, the solution which worked as correctly as it should be is
def choices_for_request(self):
category = self.request.session.get('category','')
item=self.request.GET.get('q','')
choices = self.choices.all()
if item:
choices = choices.filter(item_name__icontains=item)
super(AutoComplete, self).choices_for_request()
if category:
choices = choices.filter(product_type__name=category)
return self.order_choices(choices)[0:self.limit_choices]
I missed out
item=self.request.GET.get('q','')
autocomplete-light uses the get method and the predefined literal q to transfer the value typed by user.
I wasn't able to crack out the meaning of q. After some hit and trial, I got that it is used to store the user given value in suggestion box.

Enforce auto_now when using save(update_fields = […])

Model definitions:
class Footprint(models.Model)
date = models.DateTimeField(auto_now = True)
class Stuff(Footprint):
name = models.CharField(max_length = 255)
some_other_field = models.CharField(max_length = 255)
In a Stuff object, I'd like to only update the name field, and leave all the other fields unchanged, except those defined in the associated Footprint object.
Fields in the Footprint object are correctly updated if I don't use update_fields:
s = Stuff.objects.get(pk = 1)
s.name = 'Alexander'
s.save()
s.date # is correctly set
But if I specify fields to update, the associated Footprint is not not even saved.
s = Stuff.objects.get(pk = 1)
s.name = 'Tim'
s.save(update_fields = ['name'])
s.date # unfortunately, remains unchanged!!
I have to use update_fields to avoid interferences between several scripts.
At the same time, I'd like to always keep track of the last modification, defined by the "Footprint" object (it contains the last modification date, as well as several other fields; their update is triggered by a custom save() method).
Is there a way to force a call to Footprint.save() even if update_fields doesn't contain any field from Footprint?
Instead of rewriting the save() method, the other possibility is to add all the fields with the auto_now option to the update parameter. Like this:
some_object.save(update_fields = ['updated_field', 'auto_now_date']
It was enough to simply rewrite the Footprint model definition like this:
class Footprint(models.Model)
date = models.DateTimeField(auto_now = True)
def save(self, *args, **kwargs):
if kwargs.has_key('update_fields'):
kwargs['update_fields'] = list(set(list(kwargs['update_fields']) + ['date']))
return super(Footprint, self).save(*args, **kwargs)
Of course, if you have more fields to update than just a date field, you just have to append them in the list.
You can do this by changing your .save() method. Now, I now sure what it is that you want to do exactly, so I will leave you a template, since I believe wanting to have your own way of saving changes is indeed what do yo want.
In your class add this function definition:
def save(self, *args, **kwargs):
# Do something here, whatever it is that you want
return super(<YourClassName>, self).save(self, *args, **kwargs)
This keep the features of the normal save() function unchanged and add your custom changes to the changes you want to make as well.

Django db.connection.cursor.fetchone() returns unexpected result

I have this manager in my models.py
class ItemManager(models.Manager):
def get_fee(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT fee
FROM item
WHERE itemID = %d AND item.type = %d
""", [self.myItemID, self.myItemType])
fee = cursor.fetchone()
return fee
and class
Sample(models.Model):
sampleID = models.AutoField(primary_key=True)
itemID = models.ForeignKey(Item)
item.type = models.ForeignKey(Item)
...
def save(self, *args, **kwargs):
is_new = self.pk is None
super(Sample, self).save(*args, **kwargs)
if is_new:
cd.amount = MonthlyFeeManager()
cd.save()
Then it produces an error:
Cannot convert <myapp.models.ItemManager object at 0xa6f07ec> to Decimal
In general, i want to execute a RAW SQL query in a manager and use it to get the result from the query. I tried to search but most returns are tuples, not a value.
This is not how you use a manager. Even with a perfectly normal class instance, your attempt wouldn't give you what you wanted: you would need to instantiate it and call get_fee() on the instance.
With a manager, you don't need to instantiate it, because that's already done for you as the objects attribute on the model. But you still need to call the relevant method:
cd.amount = Sample.objects.get_fee()
However, this still won't work. That's because you've referred to self.myItemId and self.myItemType, which don't exist on the Manager object. Which leads me to the conclusion that you don't want a Manager object at all: you just want a standard model method. And there's no need for the raw SQL, either: your code is perfectly easily expressed using normal model query syntax.
(I can't show you exactly what it would look like, because the ForeignKeys in your example don't make any sense, and it's not clear where fee is supposed to be coming from.)

Django : Validate data by querying the database in a model form (using custom clean method)

I am trying to create a custom cleaning method which look in the db if the value of one specific data exists already and if yes raises an error.
I'm using a model form of a class (subsystem) who is inheriting from an other class (project).
I want to check if the sybsystem already exists or not when i try to add a new one in a form.
I get project name in my view function.
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def clean(self,project_name):
cleaned_data = super(SubsytemForm, self).clean(self,project_name)
form_subsystem_name = cleaned_data.get("subsystem_name")
Subsystem.objects.filter(project__project_name=project_name)
subsystem_objects=Subsystem.objects.filter(project__project_name=project_name)
nb_subsystem = subsystem_objects.count()
for i in range (nb_subsystem):
if (subsystem_objects[i].subsystem_name==form_subsystem_name):
msg = u"Subsystem already existing"
self._errors["subsystem_name"] = self.error_class([msg])
# These fields are no longer valid. Remove them from the
# cleaned data.
del cleaned_data["subsystem_name"]
return cleaned_data
My view function :
def addform(request,project_name):
if form.is_valid():
form=form.save(commit=False)
form.project_id=Project.objects.get(project_name=project_name).id
form.clean(form,project_name)
form.save()
This is not working and i don't know how to do.
I have the error : clean() takes exactly 2 arguments (1 given)
My model :
class Project(models.Model):
project_name = models.CharField("Project name", max_length=20)
Class Subsystem(models.Model):
subsystem_name = models.Charfield("Subsystem name", max_length=20)
projects = models.ForeignKey(Project)
There are quite a few things wrong with this code.
Firstly, you're not supposed to call clean explicitly. Django does it for you automatically when you call form.is_valid(). And because it's done automatically, you can't pass extra arguments. You need to pass the argument in when you instantiate the form, and keep it as an instance variable which your clean code can reference.
Secondly, the code is actually only validating a single field. So it should be done in a specific clean_fieldname method - ie clean_subsystem_name. That avoids the need for mucking about with _errors and deleting the unwanted data at the end.
Thirdly, if you ever find yourself getting a count of something, iterating through a range, then using that index to point back into the original list, you're doing it wrong. In Python, you should always iterate through the actual thing - in this case, the queryset - that you're interested in. However, in this case that is irrelevant anyway as you should query for the actual name directly in the database and check if it exists, rather than iterating through checking for matches.
So, putting it all together:
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def __init__(self, *args, **kwargs):
self.project_name = kwargs.pop('project_name', None)
super(SubsystemForm, self).__init__(*args, **kwargs)
def clean_subsystem_name(self):
form_subsystem_name = self.cleaned_data.get("subsystem_name")
existing = Subsystem.objects.filter(
project__project_name=self.project_name,
subsytem_name=form_subsystem_name
).exists()
if existing:
raise forms.ValidationError(u"Subsystem already existing")
return form_subsystem_name
When you do form=form.save(commit=False) you store a Subsystem instance in the variable form but the clean method is defined in SubsystemForm. Isn't it?