adding / editing django generic foreign key objects - python

Model Device:
class Device(models.Model):
device_code = models.CharField(max_length=64,unique=True)
is_enabled = models.BooleanField(default=False)
def __unicode__(self):
return u'%s: %s' % (self.device_code, 'ENABLED' if self.is_enabled else 'DISABLED')
Model AttributeValues:
class AttributeValue(models.Model):
attribute = models.ForeignKey(Attribute)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
abstract = True
unique_together = (
('attribute', 'content_type','object_id'),
)
index_together = (
('content_type','object_id'),
)
#property
def formatted(self):
"""
PLEASE SELECT RELATED ATTRIBUTE BEFORE USING THIS FUNCTION
"""
return self.attribute.format % self.value
def save(self,*args,**kwargs):
if hasattr(self.content_object,'invalidate_cache') and callable(self.content_object.invalidate_cache):
self.content_object.invalidate_cache()
super(AttributeValue,self).save(*args, **kwargs)
def __unicode__(self):
return u'%s %s' % (self.attribute.name, self.value)
class NumericAttributeValue(AttributeValue):
value = models.DecimalField(max_digits=12,decimal_places=4)
class LongTextAttributeValue(AttributeValue):
value = models.TextField()
class ShortTextAttributeValue(AttributeValue):
value = models.CharField(max_length=255)
class FileAttributeValue(AttributeValue):
attribute_file = models.FileField(upload_to="attribute_imgs")
Model Attribute:
ATTRIBUTE_TYPE_CHOICES = (
('n','Numeric'),
('s','Short Text (255)'),
('m','Long Text')
)
class Attribute(models.Model):
name = models.CharField(max_length=255)
code = models.CharField(max_length=64,unique=True)
attribute_type = models.CharField(max_length=1,choices=ATTRIBUTE_TYPE_CHOICES)
sorting_order = models.PositiveIntegerField(default=0)
show = models.BooleanField(default=False)
format = models.CharField(max_length=64,default='%s')
class Meta:
ordering = ['sorting_order','name']
def __unicode__(self):
return self.name
In my device editing (adding) page, it needs to be able to create or select an attribute, then create (or edit / delete) an attribute value (could be a numeric value, long text value, short text value or a file) associated to this attribute, and the current (or new) device. How would you create a django formset for this kind of scenario?

I had to solve a similar problem and django-polymorphic worked for me.
If you define an abstract model as the parent, then it allows you to select any child models that the parent is based on in the Django admin interface (when selecting a foreign-key for example).
You will have to make some changes in your model & admin to get it working (for eg; you won't need GenericForeignKey).
https://django-polymorphic.readthedocs.org/en/latest/

Related

how can we query foreign key and get results of objects which are connected to foreign key

How can I can use managers to query my foreign key and then retrieve objects that are connected to foreign key?
Here is my models.py:
from django.db import models
# Create your models here.
class BookManager(models.Manager):
def title_count(self,keyword):
return self.filter(title__icontains=keyword).count()
class CategoryManager(models.Manager):
def category_count(self):
return self.filter(category__icontains=python).count()
class Category(models.Model):
title=models.CharField(max_length=20)
def __str__(self):
return self.title
class Enquiry(models.Model):
title=models.CharField(max_length=200)
category=models.ForeignKey(Category ,default=False,blank=False)
detail=models.TextField()
objects = BookManager()
objects=CategoryManager()
# tags=models.ChoiceField()
def __str__(self):
return self.title
I tried to use category manager but it gave me a strange error.
I just want to know how exactly we can get the objects that are connected with category foriegn-key and show them as list to the users.
You can combine both the title_count and category_count methods under one Manager. When filtering through a ForeignKey, your field lookup needs to specify the name of the field you're trying to filter on, else it will assume you're querying the ID field. So instead of category__icontains, you would do category__title__icontains.
Another note, in newer versions of Django, you are required to specify the on_delete parameter when defining a ForeignKey.
This is a working example of what I think you're trying to accomplish.
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
def category_count(self, keyword):
return self.filter(category__title__icontains=keyword).count()
class Category(models.Model):
title = models.CharField(max_length=20)
def __str__(self):
return self.title
class Enquiry(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(Category,
default=False,
blank=False,
on_delete=models.CASCADE)
detail = models.TextField()
books = BookManager()
# tags=models.ChoiceField()
def __str__(self):
return self.title
Here's how you would use it:
Enquiry.books.category_count('python')
Enquiry.books.title_count('test')

Django Model Form Many to Many field is not displaying value

I have a many to many field and a foreign key field on a model form. It appears to be making the right query but the result are Objects and not the values of the object.
Do I need to overwrite the queryset using a VALUES_LIST method?
forms.py
class Meta:
model = AccountParameters
fields =['acctFilterName', 'excludeClassification', 'tradingCash',]
#exclude = ['acctFilterName']
labels = {
'acctFilterName': _('Account Filters:'),
'excludeClassification': _('Exclude Classifications: '),
'tradingCash':_('Remove accounts whose trading cash < % of AUM: ')
}
models.py
class AccountParameters(models.Model):
acctFilterName = models.ForeignKey(AccountFilters)
excludeClassification = models.ManyToManyField(ClassificationNames)
tradingCash = models.FloatField()
You have to add a method to represent your object with a string :
If you are using python 2, use __unicode__ and on python 3 use : __str__ .
E.g (with python 2) :
class AccountFilters(models.Model):
name = models.CharField(max_length=255)
# other attributes
def __unicode__(self):
return self.name
class AccountParameters(models.Model):
acctFilterName = models.ForeignKey(AccountFilters)
excludeClassification = models.ManyToManyField(ClassificationNames)
tradingCash = models.FloatField()
def __unicode__(self):
return self.acctFilterName.name
E.g (with python 3) :
class AccountFilters(models.Model):
name = models.CharField(max_length=255)
# other attributes
def __str__(self):
return self.name
class AccountParameters(models.Model):
acctFilterName = models.ForeignKey(AccountFilters)
excludeClassification = models.ManyToManyField(ClassificationNames)
tradingCash = models.FloatField()
def __str__(self):
return self.acctFilterName.name

Django: Creating editable default values for model instance based on foreignkey existance

I'm playing around in Django, and wondering if there is a way to loop through instances of two different models I have created?
/ models.py:
class Tran(models.Model):
name = models.CharField(max_length=300)
description = models.CharField(max_length=2000)
type = models.ForeignKey(TransactionType)
def __str__(self):
return self.name
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
What I want to do:
Look through each of the Tran instances and create a default value for the links/displays in the DocLink table instead of doing it manually.
Is there anyway I can be pointed in the right direction?
If you want to set links/displays default value in DocLink instance based on trann field you can override model's save method.
For example following code shows how to set t_link if it doesn't have a value:
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
def save(self, *args, **kwargs):
if not self.t_link:
pass # TODO set self.t_link based on trann
super(DocLink, self).save(*args, **kwargs)
Also you can change model's trann field to:
trann = models.ForeignKey(Transaction, related_name="doclinks")
And then access to all DocLinks of a Tran with:
# t is an instance of Tran class
t.doclinks.all()
So you can loop through this list and do what you want.

How to store functions in django models

edit: I completely rewrote the question as the original one didn't clearly explain my question
I want to run a function which is specific to each particular model instance.
Ideally I want something like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.FunctionField() #stores a function specific to this instance
x = MyModel(data='originalx', perform_unique_action=func_for_x)
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModel(data='originaly', perform_unique_action=func_for_y)
y.perform_unique_action() #will do whatever is specified for instance y
However there is no datatype FunctionField. Normally this would be solvable with inheritance, and creating subclasses of MyModel, maybe like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class MyModelX(MyModel):
perform_unique_action = function_X
class MyModelY(MyModel):
perform_unique_action = function_Y
x = MyModelX(data='originalx')
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModelY(data='originaly')
y.perform_unique_action() #will do whatever is specified for instance y
Unfortunately, I don't think I can use inheritance because I am trying to access the function this way:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel = SecondModel.objects.get(other_data=3)
secondmodel.mymodel.perform_unique_action()
The problem seems to be that I don't know what type the foreign key is going to be in SecondModel if I override the perform_unique_action in subclasses.
Can I access MyModel from SecondModel as a foreign key and still have a unique function for each instance of MyModel?
This works for me. I haven't tested it, but you should be able to create another class and override their methods and it'll work. Check the class Meta line, it'll treat it as an abstract class. Here's an example of my actual classes that I'm working on right now.
EDIT: Added VoteComment class and tested it. It works as expected!
class Vote(models.Model):
VOTE_ENUM = (
(VoteEnum.DOWN_VOTE, VoteEnum.toString(VoteEnum.DOWN_VOTE)),
(VoteEnum.NONE, VoteEnum.toString(VoteEnum.NONE)),
(VoteEnum.UP_VOTE, VoteEnum.toString(VoteEnum.UP_VOTE)),
)
question = models.ForeignKey(Question, null=False, editable=False, blank=False)
voter = models.ForeignKey(User, blank=False, null=False, editable=False)
vote_type = models.SmallIntegerField(default=0, null=False, blank=False, choices=VOTE_ENUM)
class Meta:
abstract = True
def is_upvote(self):
return self.vote_type > 0
def is_downvote(self):
return self.vote_type < 0
class VoteAnswer(Vote):
answer = models.ForeignKey(Answer, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "answer"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, answer.text[:32])
def is_upvote(self):
return "FOO! "+str(super(VoteAnswer, self).is_upvote())
class VoteComment(Vote):
comment = models.ForeignKey(Comment, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "comment"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, comment.text[:32])
def is_upvote(self):
return "BAR!"
I came up with two ways of having a specific function defined for each object. One was using marshal to create bytecode which can be stored in the database (not a good way), and the other was by storing a reference to the function to be run, as suggested by Randall. Here is my solution using a stored reference:
class MyModel(models.Model):
data = models.CharField(max_length=100)
action_module = models.CharField(max_length=100)
action_function = models.CharField(max_length=100)
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel_obj = SecondModel.objects.get(other_data=3)
#The goal is to run a function specific to the instance
#of MyModel referred to in secondmodel_obj
module_name = secondmodel_obj.mymodel.action_module
func_name = secondmodel_obj.mymodel.action_function
module = __import__(module_name)
func = vars(module)[func_name]
func()
Thanks to everyone who replied, I couldn't have got to this answer if it weren't for your help.
You could achive some similar behavior overriding the save method. And providing special callbacks to your instances.
Something like:
def default_function(instance):
#do something with the model instance
class ParentModel(model.Model):
data = models.CharField()
callback_function = default_function
def save(self, *args, **kwargs):
if hasattr(self, 'callback_function'):
self.callback_function(self)
super(ParentModel, self).save(*args, **kwargs)
class ChildModel():
different_data = models.CharField()
callback_function = other_fun_specific_to_this_model
instance = ChildModel()
#Specific function to this particular instance
instance.callback_function = lambda inst: print inst.different_data
instance.save()
You can write endpoints on your server and limit their access to just your self. Then store in each model instance corresponding url. For example:
views.py
def funx_x(request):
pass
def func_y(request):
pass
models.py:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.URLField()
and then:
x = MyModel(data='originalx', perform_unique_action='http://localhost/funx_x')
requests.post(x.perform_unique_action)
i dont know whether i understand u correct or not. but you can check out this example here.
Example:
A string representing an attribute on the model. This behaves almost the same as the callable, but self in this context is the model instance. Here's a full model example:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'decade_born_in')

How do I filter values in a Django form using ModelForm?

I am trying to use the ModelForm to add my data. It is working well, except that the ForeignKey dropdown list is showing all values and I only want it to display the values that a pertinent for the logged in user.
Here is my model for ExcludedDate, the record I want to add:
class ExcludedDate(models.Model):
date = models.DateTimeField()
reason = models.CharField(max_length=50)
user = models.ForeignKey(User)
category = models.ForeignKey(Category)
recurring = models.ForeignKey(RecurringExclusion)
def __unicode__(self):
return self.reason
Here is the model for the category, which is the table containing the relationship that I'd like to limit by user:
class Category(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User, unique=False)
def __unicode__(self):
return self.name
And finally, the form code:
class ExcludedDateForm(ModelForm):
class Meta:
model = models.ExcludedDate
exclude = ('user', 'recurring',)
How do I get the form to display only the subset of categories where category.user equals the logged in user?
You can customize your form in init
class ExcludedDateForm(ModelForm):
class Meta:
model = models.ExcludedDate
exclude = ('user', 'recurring',)
def __init__(self, user=None, **kwargs):
super(ExcludedDateForm, self).__init__(**kwargs)
if user:
self.fields['category'].queryset = models.Category.objects.filter(user=user)
And in views, when constructing your form, besides the standard form params, you'll specify also the current user:
form = ExcludedDateForm(user=request.user)
Here example:
models.py
class someData(models.Model):
name = models.CharField(max_length=100,verbose_name="some value")
class testKey(models.Model):
name = models.CharField(max_length=100,verbose_name="some value")
tst = models.ForeignKey(someData)
class testForm(forms.ModelForm):
class Meta:
model = testKey
views.py
...
....
....
mform = testForm()
mform.fields["tst"] = models.forms.ModelMultipleChoiceField(queryset=someData.objects.filter(name__icontains="1"))
...
...
Or u can try something like this:
class testForm(forms.ModelForm):
class Meta:
model = testKey
def __init__(self,*args,**kwargs):
super (testForm,self ).__init__(*args,**kwargs)
self.fields['tst'].queryset = someData.objects.filter(name__icontains="1")
I know this is old; but its one of the first Google search results so I thought I would add how I found to do it.
class CustomModelFilter(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s %s" % (obj.column1, obj.column2)
class CustomForm(ModelForm):
model_to_filter = CustomModelFilter(queryset=CustomModel.objects.filter(active=1))
class Meta:
model = CustomModel
fields = ['model_to_filter', 'field1', 'field2']
Where 'model_to_filter' is a ForiegnKey of the "CustomModel" model
Why I like this method:
in the "CustomModelFilter" you can also change the default way that the Model object is displayed in the ChoiceField that is created, as I've done above.
is the best answer:
BookDemoForm.base_fields['location'] = forms.ModelChoiceField(widget=forms.Select(attrs={'class': 'form-control select2'}),queryset=Location.objects.filter(location_for__fuel=True))

Categories