Python convert string to class - python

I want to do a query on the django User table like this:
u = User.objects.filter(member__in = member_list)
where:
class Member(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField('Date of Birth', blank=True, null=True)
and member_list is a list of eligible members.
The query works fine but the problem is I do not actually know the model member is called member. It could be called anything.
I store the name of the model I want in a model called Category. I have a link to the name of the model through content_type.Category is defined as:
class Category(models.Model):
name = models.CharField('Category', max_length=30)
content_type = models.ForeignKey(ContentType)
filter_condition = JSONField(default="{}", help_text=_(u"Django ORM compatible lookup kwargs which are used to get the list of objects."))
user_link = models.CharField(_(u"Link to User table"), max_length=64, help_text=_(u"Name of the model field which links to the User table. 'No-link' means this is the User table."), default="No-link")
def clean (self):
if self.user_link == "No-link":
if self.content_type.app_label == "auth" and self.content_type.model == "user":
pass
else:
raise ValidationError(
_("Must specify the field that links to the user table.")
)
else:
if not hasattr(apps.get_model(self.content_type.app_label, self.content_type.model), self.user_link):
raise ValidationError(
_("Must specify the field that links to the user table.")
)
def __unicode__(self):
return self.name
def _get_user_filter (self):
return str(self.content_type.app_label)+'.'+str(self.content_type.model)+'.'+str(self.user_link)+'__in'
def _get_filter(self):
# simplejson likes to put unicode objects as dictionary keys
# but keyword arguments must be str type
fc = {}
for k,v in self.filter_condition.iteritems():
fc.update({str(k): v})
return fc
def object_list(self):
return self.content_type.model_class()._default_manager.filter(**self._get_filter())
def object_count(self):
return self.object_list().count()
class Meta:
verbose_name = _("Category")
verbose_name_plural = _("Categories")
ordering = ('name',)
So I can retrieve the name of the model that links to User but I then need to convert it into a class which I can include in a query.
I can create an object x = category.content_type.model_class() which gives me <class 'cltc.models.Member'> but when I them perform a query s = User.objects.filter(x = c.category.object_list()) I get the error Cannot resolve keyword 'x' into field.
Any thoughts most welcome.

The left hand side of the filter argument is a keyword, not a python object, so x is treated as 'x', and Django expects a field called x.
To get around this, you can ensure that x is a string, and then use the python **kwarg syntax:
s = User.objects.filter(**{x: c.category.object_list()})
Thanks to https://stackoverflow.com/a/4720109/823020 for this.

Related

Django Filter name "todo__test" is not defined

I'm trying to filter my Todos by the test_id pulled from the URL. It pulls the id from the URL but it cant seem to filter with todo__test. I have also tried "test", "Todo.test.test_id", "Todo.test". I guess I'm confused about what variable I need to filter and the Django restframework documentation doesn't explicitly show what variable to use. Their example uses "purchaser__username" which I don't understand where it comes from. https://www.django-rest-framework.org/api-guide/filtering/
class TodoList(generics.ListAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
def get_queryset(self):
test_id = self.kwargs['test_id']
return Todo.objects.filter(todo__test == test_id)
class Todo(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
content = models.TextField(blank=True)
order = models.IntegerField()
def __str__(self):
return self.content + ' - ' + self.test.test_name
class Meta:
ordering = ['test_id']
i guess it will be like this. you passed incorrect foreign key field name.
Todo.objects.filter(test_id='whatever_value')

Cannot assign foreign key with item ID

I am trying to auto assign a value to a foreign key, so that the model is automatically associated with another model. This is done when an entry is made by a form. I get the following error
ValueError at /nodiso/createaction/23/
Cannot assign "'23'": "LeadActions.lead" must be a "Leads" instance.
This is the two models:
class Leads(models.Model):
company = models.ManyToManyField(Company)
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
name = models.CharField(max_length=265)
email = models.EmailField(max_length=265)
tel = models.IntegerField()
dateenq = models.DateField(auto_now_add=True,null=True)
def get_absolute_url(self):
return reverse('nodisoapp:leadlist')
def __str__(self):
return self.name
class LeadActions(models.Model):
lead = models.ForeignKey(Leads)
name = models.CharField(max_length=265)
crdate = models.DateField(auto_now_add=True)
Duedate = models.DateField()
creator = models.CharField(max_length=265)
overdue = models.IntegerField(null=True,blank=True)
def get_absolute_url(self):
return reverse('nodisoapp:leadlist')
def __str__(self):
return self.name
This is the View
class ActionCreateView(LoginRequiredMixin, generic.CreateView):
login_url = '/scrty/login/'
template_name = "nodiso/actioncreate.html"
form_class = forms.LeadActionCreateForm
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.lead = self.kwargs['pk']
self.object.creator = self.request.user
self.object.save()
return super(LeadCreateView, self).form_valid(form)
This is the model form
class LeadActionCreateForm(forms.ModelForm):
class Meta:
model = models.LeadActions
fields = ['name','Duedate']
I would appreciate the help.
Try this:
self.object.lead_id = self.kwargs['pk']
Since you are using the key value directly, use need to reference the variable with '_id'.
Another way is:
self.object.lead_id = Lead.objects.get(pk=self.kwargs['pk'])
Which in this case is not required, since you have the key value.
You have two problems here.
One, you need to use the lead_id field name if you have the PK of the related instance rather than an instance object.
Two, kwargs matched from the URI regexp are strings, while PK values are integers. Assuming your uris regexp is properly restrictive, you can just convert it using int. So combining the two:
self.object.lead = int(self.kwargs['pk'])
Instead of
self.object.lead = self.kwargs['pk']
try doing
lead_id = int(self.kwargs['pk'])
self.object.lead = Lead.objects.get(pk=lead_id)
When using the field name to assign relationship fields in Django, you must pass a model instance not a primary key. You could also use the primary key by doing:
lead_id = int(self.kwargs['pk'])
self.object.lead_id = lead_id

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

How to save a many to many model in django

I have the following two model class in django.
class Rule(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User, related_name='rules', null=True, blank=True)
threshold = models.CharField(max_length=50, null=True, blank=True)
alert_value = models.CharField(max_length=50, null=True, blank=True)
is_internal = models.BooleanField(default=False)
def __unicode__(self):
return self.name
def to_json(self):
return {
'name': self.name,
'threshold': self.threshold,
'alert_value': self.alert_value
}
class Module(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(null=True, blank=True)
is_internal = models.BooleanField(default=False)
rules = models.ManyToManyField(Rule)
def to_json(self):
return {
'name': self.name,
'description': self.description,
'rules': [r.to_json() for r in self.rules.all()]
}
def __unicode__(self):
return self.name
Now I have the following code to save a Module object which implicitly contains a rules object in my view.py
def create_module(request):
if request.method == 'POST':
module_name = request.POST.get('name')
module_description = request.POST.get('description')
rule_ids = request.POST.getlist('rule_id')
rules = None
for rule_id in rule_ids:
try:
rules = models.Rule.objects.filter(pk__in=rule_id)
except models.Rule.DoesNotExist:
pass
module = models.Module(name=module_name,
description=module_description,
rules=rules)
module.save()
I get the rules correctly here but when save gets called I get an error
Exception Type: TypeError at /modules/create/
Exception Value: 'rules' is an invalid keyword argument for this function
How to overcome this when I want to save an object graph.
rules is not really a field on the model, it's an entry in a linking table - so it can't be saved until the Module entry exists. Also note that your loop is such that it will never consist of more than one Rules object, because you overwrite the rules variable each time. Instead you should simply get all the Rules and add them in one go.
module = models.Module(name=module_name,
description=module_description)
module.save()
rules = models.Rule.objects.filter(pk__in=rule_ids)
module.rules = rules
There's no need to save again after that: assigning to a related queryset does the database operation automatically. Also note that filter will not raise a DoesNotExist exception: if there is no matching rule, then there simply won't be an element in the resulting queryset.
you are overriding the rules queryset inside try and filter() doesnot raise DoesNotExist exception btw..
try this:
module = models.Module(name=module_name,description=module_description)
module.save()
#first save 'module' and add 'rules' from filter()-result
rules = models.Rule.objects.filter(pk__in=rule_ids)
module.rules = rules
module.save()
more about how to save m2m in django

Display relevant records from many to many in django-tables2

OK, so I have an Item class that has a many-to-many attribute to User through a 'Roles' class. I am trying to create a django-table for the Items such that out of any of the roles attached to the item, if the current User is attached to that role, the name of the role displays. I hope that makes some sort of sense. Here's what I have so far, which I didn't really expect to work because I don't see how the Table class can know about the request/user. I'm stuck.
models.py
class Item(models.Model):
name = models.CharField(max_length=255)
owner = models.ForeignKey(User, related_name='Owner')
roles = models.ManyToManyField(User, through='Role')
class Role(models.Model):
role_type = models.ForeignKey(RoleType)
user = models.ForeignKey(User)
item = models.ForeignKey(Item)
tables.py
class OwnedTable(tables.Table):
roles = tables.Column()
user = request.user
def render_roles(self):
for role in roles:
if role.User == user:
return role.role_type
else:
pass
class Meta:
model = Item
attrs = {"class": "paleblue"}
fields = ('id', 'name', 'owner', 'roles')
You can get the request object from self.context. So if you only need request.user, that's one way to do it.
class OwnedTable(tables.Table):
roles = tables.Column(empty_values=())
def render_roles(self):
user = self.context["request"].user
...
Otherwise, #mariodev's solution works.
It seems like there's no way of using auth user without some overriding.
You can override __init__ for our table class like this:
class OwnedTable(tables.Table):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(OwnedTable, self).__init__(*args, **kwargs)
then, inside view, you call table with user argument, like so
table = OwnedTable(Person.objects.all(), user=request.user)
now you can use self.user inside render_roles method to refer to the currently logged in user.
Another solution is shown on https://github.com/bradleyayers/django-tables2/issues/156, which worked for me after some adjustments for my setup.
Given that a Person would have an M2M to Contacts, and you want to display all contacts for that Person in django-tables2, then the following would do:
class PersonTable(tables.Table):
person_contacts = tables.Column(accessor="contacts", verbose_name="Contacts")
def render_person_contacts(self, value, table):
clist = ""
cfirst = True
conts = list(value.all())
for c in conts:
if not cfirst:
clist += "<br />"
else:
cfirst = False
print c.id
uri = reverse('cont_detail', kwargs={'pk': c.id})
clist += '' + c.name + '' + ' (' + c.relation + ')'
return mark_safe(clist)
You basically add a column with a non-existent name, set the accessor to the M2M field name, then call render_<newly_added_column> where you obtain what you need from value.

Categories