I use Django 2.1.
I have a general utility/function(using celery) that I call from the save method of a Model(is an abstract Model, inherited by other Models).
Because the data from the function will be serialize, I pass to it the Model name and PK.
class A(models.Model)
def save(self, *args, **kwargs):
send_async(model_name=type(self).__name__, pk=self.id))
class B(A)
class C(A)
Later, I want to query the Model, but I don't know how to get the Model:
I tried:
model = apps.get_model('django_app_name.{}'.format(model_name))
I get the following error:
No installed app with label 'django_app_name'.
Also:
model = ContentType.objects.get(model=model_name)
This doesn't trow an error, but very strange behavior 'recalls' the View with different arguments, and off course no results.
Use the name of the app which has the model, not django_app_name.
To get the model name, try this:
send_async(model_name=self.__class__.__name__, pk=self.id)
instance.__class__.__name__ will return the class name
You should do like so:
class A(models.Model)
def save(self, *args, **kwargs):
send_async(
model_name=self._meta.model_name,
app_label=self._meta.app_label,
pk=self.id
)
And in send_async:
def send_async(model_name, app_label, pk):
content_type = ContentType.objects.get(app_label=app_label, model=model_name)
instance = content_type.get_object_for_this_type(id=pk)
...
For more information about it see https://docs.djangoproject.com/en/2.1/ref/contrib/contenttypes/
Related
I made one model(ModelA) in which 2 choices present there, I am inheriting this model, in the other two models
CHOICES = (("work", "work"), ("Home", "Home"))
class ModelA(models.Model):
type_of_address = models.CharField(choices=CHOICES)
...
class ForWorkModel(ModelA):
type_of_address--->work
class ForHomeModel(ModelA):
type_of_address--->Home
I want to inherit the model and want to set some field values, as I mentioned in the code.
Is there any way?
You could add a custom save on each of the individual models to set the desired string:
class ForWorkModel(ModelA):
def save(self, *args, **kwargs):
if not self.pk: # only change if the object is new
self.type_of_address = "work" # or CHOICES[0][0]
super().save(*args, **kwargs)
or, I'm not 100% sure, but this might work:
class ModelA(models.Model):
type_of_address = models.CharField(choices=CHOICES, default=cls.get_default_address)
class ForWorkModel(ModelA):
def get_default_address():
return "work"
I have a custom queryset on a model manager:
class TenantManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(myfield=myvalue)
class TenantModel(TenantModelMixin, models.Model):
objects = TenantManager()
class Meta:
abstract = True
I use the abstract TenantModel as a mixin with another model to apply the TenantManager. E.g.
class MyModel(TenantModel):
This works as expected, applying the TenantManager filter every time MyModel.objects.all() is called when inside a view.
However, when I create a ModelForm with the model, the filter is not applied and all results (without the filter are returned. For example:
class AddPersonForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('person', )
Why is this and how to I ensure the ModelManager is applied to the queryset in ModelForm?
Edit
#Willem suggests the reason is forms use ._base_manager and not .objects (although I can not find this in the Django source code), however the docs say not to filter this kind of manager, so how does one filter form queries?
Don’t filter away any results in this type of manager subclass
This
manager is used to access objects that are related to from some other
model. In those situations, Django has to be able to see all the
objects for the model it is fetching, so that anything which is
referred to can be retrieved.
If you override the get_queryset() method and filter out any rows,
Django will return incorrect results. Don’t do that. A manager that
filters results in get_queryset() is not appropriate for use as a base
manager.
You can do it in two ways:
First: When creating the form instance, add the queryset for the desired field.
person_form = AddPersonForm()
person_form.fields["myfield"].queryset = TenantModel.objects.filter(myfield="myvalue")
Second: Override the field's queryset in the AddPersonForm itself.
class AddPersonForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('person', )
def __init__(self, *args, **kwargs):
super(AddPersonForm, self).__init__(*args, **kwargs)
self.fields['myfield'].queryset = TenantModel.objects.filter(myfield="myvalue")
I'm not sure why your code doesn't properly works. Probably you haven't reload django app. You could load queryset in __init__ of your form class
class AddPersonForm(forms.ModelForm):
person = forms.ModelMultipleChoiceField(queryset=None)
class Meta:
model = MyOtherModel
fields = ('person', )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['person'].queryset = MyModel.objects.all()
I have the following situation
I have a manager class that filters a queryset according to a field. The problem is that the field name is different according to the class but the value to which it filters comes from the same place (so i thought i don't need several managers). This is what i did so far.
class MyManager(models.Manager):
def __init__(self, field_name):
super(MyManager, self).__init__()
self.field_name = field_name
def get_queryset(self):
# getting some value
kwargs = { self.field_name: some_value }
return super(MyManager, self).get_queryset().filter(**kwargs)
class A:
# some datamembers
#property
def children(self):
return MyUtils.prepare(self.b_set.all())
class B:
objects = MyManager(field_name='my_field_name')
a = models.ForeignKey(A, null=False, blank=False)
When i run tests i that retrieve from the DB a B object, and try to read the children property i get the following error:
self = <django.db.models.fields.related_descriptors.RelatedManager object at 0x7f384d199290>, instance = <A: A object>
def __init__(self, instance):
> super(RelatedManager, self).__init__()
E TypeError: __init__() takes exactly 2 arguments (1 given)
I know its because of the constructor parameter because when i remove it (or give it a default value) all of the tests work.
How can i overcome this? Is this the right way of achieving this?
Tech stuff:
Django 1.9.5
test framework py.test 2.9.1
Thanks
Another option would be to generate the Manager class dynamically, such as:
def manager_factory(custom_field):
class MyManager(models.Manager):
my_field = custom_field
def get_queryset(self):
# getting some value
kwargs = {self.my_field: 'some-value'}
return super(MyManager, self).get_queryset().filter(**kwargs)
return MyManager()
class MyModel(models.Model):
objects = manager_factory('custom_field')
This way you can decouple the Manager from the Model class.
As you can see, that error is happening because Django instantiates a new Manager whenever you make a related objects call; that instantiation wouldn't get the extra parameter.
Rather than getting the value this way, you could try making it an attribute of the model and then referencing it via self.model.
class MyManager(models.Manager):
def get_queryset(self):
# getting some value
kwargs = { self.model.manager_field_name: some_value }
class B:
manager_field_name = 'my_field_name'
objects = MyManager()
I would like to create a dynamic Form based on a ModelForm. The aim is to add fields with the information in the json field.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['name', 'json']
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
[ Create fields here]
I can create the fields dynamically like this:
variables = ('var_1', 'var_2',)
for v in variables:
self.fields[v] = forms.CharField(label=v)
Now, I would like to replace variables with the json.variables value. I tried this: self.fields['json'].initial, self.fields['json'].data, self.fields['json'].cleaned_data without success.
Do you know how can I have access to the model value?
Finally the solution is quite easy. We just have to use self.instance.json.
This seems like a bug but I just want to make sure I'm consuming the API properly.
It seems that support for django's modelform isn't supported on neo4django. Here's what I have:
Simple class:
from neo4django.db import models
class Person(models.NodeModel):
name = models.StringProperty()
The modelform:
class PersonForm(forms.ModelForm):
class Meta:
model = Person
Will trigger exception:
'super' object has no attribute 'editable'
I posted details as an issue:
https://github.com/scholrly/neo4django/issues/135
Because when Django goes to lookup field information using the model's _meta information, it finds a BoundProperty instead of a StringProperty or Property (which has a member called 'editable', but BoundProperty doesn't).
Is there a workaround, or is this an actual bug? Any ideas on how to fix the bug? I'm not familiar with the library codebase.
Thanks!
Below is a reasonable (and quick) workaround for anyone using neo4j with Django.
This solution requires that field names on the form have the exact same name as the attributes of the model.
Inherit the form from this class and set the model under the form class Meta class:
class NeoModelForm(forms.Form):
def __init__(self, *args, **kwargs):
super(NeoModelForm, self).__init__(*args, **kwargs)
self._meta = getattr(self, 'Meta', None)
if not self._meta:
raise Exception('Missing Meta class on %s' % str(self.__class__.__name__))
if not hasattr(self._meta, 'model'):
raise Exception('Missing model on Meta class of %s' % str(self.__class__.__name__))
def save(self, commit=True):
if not self.is_valid():
raise Exception('Failed to validate')
instance = self._meta.model(**self.cleaned_data)
if commit:
instance.save()
return instance
Now you can create a form class like this:
class PersonForm(NeoModelForm):
name = forms.CharField(widget=forms.TextInput())
class Meta:
model = Person
And still be able to save a model instance from a valid form:
form = formclass(request.POST)
if form.is_valid():
obj = form.save()
Plus the commit argument will give you the same solution as django's modelform class- but I didn't bother to implement to save_m2m functionality (which doesn't seem relevant for neo4j as a backend).