Repeated fields in django models - django

I need to make a model that has 15 similar fields (let's call them field_01, field_02, field_03, etc.). I also need these fields to be easily available (e.g. to be able to do things like obj.field_01). Because I wanted to get something off the ground really quickly, I just created them the stupid way:
class M (models.Model):
....
field_01 = models.NullBooleanField()
field_02 = models.NullBooleanField()
....
I searched online for a better way to do this. Some people say use setattr, but as far as I could tell from reading and trying some stuff out, this adds attributes to an instance of a class, not the class itself, which means that when I try to attach a model form to this model, it will not see the fields added with setattr. Then I tried overriding the __new__ function, which would allow me to add properties to a class before an instance of that class is created. But I wasn't able to figure out how to do this exactly.
So, what's a way to generate these fields without breaking South and model forms and without copy-paste?

It's hard to say definitively without a concrete example of what your doing, but generally, if you find yourself repeating a field, then it's a clear sign for a one-to-many or many-to-many relationship, instead:
One-to-Many
class Field(models.Model):
m = models.ForeignKey(M, related_name='fields')
field = models.NullBooleanField()
Many-to-Many
class Field(models.Model):
field = models.NullBooleanField()
class M(models.Model):
fields = models.ManyToManyField(Field)

Django models have an add_to_class method you could (ab)use for monkey-patching models the way you would like to do.
for i in range(1, 10):
M.add_to_class('field_%s' % s, NullBooleanField())

It sounds like you are looking to have an EAV style database. You should try a library instead of rolling your own. To that end, Django EAV looks pretty awesome. https://github.com/mvpdev/django-eav
To read more about pros and cons of a bunch of libraries to accomplish this check out: https://stackoverflow.com/a/7934577/884453

Related

Is there any possibility to add fields inside the fields of a Model

Assume I have a model named MyModel and I have a Field Named field Now I want to add three more fields inside the prescription like one field_a , field_b and field_c .
Does Django Allow that in any way or we have to make another model for this purpose and then link with Foreign Key to MyModel?
Well I think it is an idea that could lead to some really hard to maintain code and should be well thought through.
That aside If you have a look at:
https://docs.wagtail.io/en/stable/topics/streamfield.html
It's a special field meant for cms applications that uses json in the field to model dynamic fields and validations for it.
Also if you run postgres this might help. https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/fields/#jsonfield

Prefetch queryset when related_name="+"

Is it possible without related name (related_name="+") to prefetch objects on the target instance? Sure I know it's not a problem with the related name, but I'm not really sure if it's possible without it.
Here is the example code:
from django.db import models
class Parent(models.Model):
name = models.CharField(max_length=50)
class Child(models.Model):
parent = models.ForeignKey(to=Parent, related_name="+", on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Parent.objects.all().prefetch_related('child_set')
Maybe it's possible using the Prefetch(lookup, queryset=None, to_attr=None) object, because it takes the queryset in the argument list?
Looked through the code a bit and found this line:
rel_obj_descriptor = getattr(instance.__class__, through_attr, None)
Here instance is the model instance, and through_attr is the field name of related instance to be fetched. This line basically tries to get a related descriptor to perform the prefetch query. In your case rel_obj_descriptor would contain None.
To answer your question no it is not possible at least for a Foreign Key, there may be some hack for Many to Many relationships as Django appears to use some internal descriptors for them.
I would advice you to simply not set related_name="+" since you want to use the backwards relation here. You say "It's because of separation of concerns between multiple apps" but that does not make much sense. Don't we set a foreign key to the user model for various other models anyway and still use the related name? Does the point of separation of concerns arise there(the user model is in a separate app)?
try
parent = Parent.objects.get(id=pk)
parent.child_set.all()
I don't know if having related_name = '+' prevents this situation, but if you never define related_name, you can definitely use this method.

Building a list of ModelForms based on relation

I think this is best explained with some simple model code (I'm writing this from scratch so possible syntax issues - unimportant here):
class Car(models.Model)
make = models.CharField(...)
model = models.CharField(...)
class StatisticType(models.Model):
name = models.CharField(...)
class Statistic(models.Model)
car = models.ForeignKey('Car')
stype = models.ForeignKey('StatisticType')
data = models.CharField(...)
class Meta:
unique_together = (('car', 'stype'),)
We have a car with some hard-coded stats and we have some database controlled statistics. I might add Colours, Wheel Size, etc. The point is it's editable from the admin so neither I or the client need to climb through the data, but it's limited so users can only pick one of each stat (you can't define "Colours" twice).
So I'm trying to write the data input form for this now and I want a list of optional ModelForms that I can chuck on the page. I've got the simplest ModelForm possible:
class StatisticForm(forms.ModelForm):
class Meta:
model = Statistic
The tricky part (in my head) is generating an instance of this ModelForm for each StatisticType, regardless of it existing yet. That is to say if a Car object doesn't have a Colour assigned to it, the form still shows. Similarly, if it does, that instance of a Statistic is loaded in the ModelForm.
In my view, how do I generate a list of these things, regardless of there being a pre-existing instance of any given Statistic?
This seems like it should be a stupidly simple thing to do but it's late on Friday and everything looks skwonky.
Sounds like you might want to leverage an inline model formset factory.
That would allow you to create as many instances of your Statistic object as you need. If you're needing to create instances of your StatisticType on the fly, that's a bit different.
When Django instantiates forms, for a foreign key, m2m or choice field, it will only accept choices that it deems "valid", and will complain if you add a choice using JavaScript that doesn't exist in a related model or set of choices server-side.
So, if you need to make StatisticTypes on the fly, and then populate formset instances with this new value, I would suggest using Knockout.js. It's very good at keeping lots of DOM elements in sync when data changes.

Django preferences/Boolean fields

I have a model that requires quite a few BooleanFields, they could be considered preferences in some way. What I'm currently doing is adding all the fields to the model itself and hardcoding the labels to the HTML. So my model looks somewhat like this:
class Project(models.Model):
...some fields...
Check1=models.BooleanField()
....many of these...
Check19=models.BooleanField()
I feel this is a very dumb way of doing it and am looking for a more Pythonic solution. Maybe ManyToMany fields? Any ideas?
I would rather give the boolean fields some meaningful names and would put verbose_name to the field description, e.g.
class Project(models.Model):
# ...
featured = models.BooleanField(_("Featured"))
published = models.BooleanField(_("Published"))
# ...
If they are all of the same type, it would probably make sense to make it a M2M, e.g. phone1, phone2, phone3, etc. would be more appropriate as phones pointing to a Phone model.
However, since these are booleans, I doubt that's the case. Having a lot of fields on a model is not a problem, if they all make sense there. It would be wrong, in fact, to abstract them away, when they have no relation to each other.
Take a look at the zen of python:
Especially:
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.

Making a fairly complex Django model method sortable in admin?

I have a reasonably complex custom Django model method. It's visible in the admin interface, and I would now like to make it sortable in the admin interface too.
I've added admin_order_field as recommended in this previous question, but I don't fully understand what else I need to do.
class Book(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=200)
library_id = models.CharField(max_length=200, unique=True)
def current_owner(self):
latest_transaction = Transaction.objects.filter(book=self)[:1]
if latest_transaction:
if latest_transaction[0].transaction_type==0:
return latest_transaction[0].user.windows_id
return None
current_owner.admin_order_field = 'current_owner'
Currently, when I click on the current_owner field in the admin interface, Django gives me
FieldError at /admin/books/book/
Cannot resolve keyword 'current_owner' into field
Do I need to make a BookManager too? If so, what code should I use? This isn't a simple Count like the example in the previous question, so help would be appreciated :)
Thanks!
The Django admin won't order models by the result of a method or any other property that isn't a model field (i.e. a database column). The ordering must be done in the database query, to keep things simple and efficient.
The purpose of admin_order_field is to equate the ordering of a non-field property to the ordering of something that is a field.
For example, a valid values current_owner.admin_order_field could be id, title or library_id. Obviously none of these makes sense for your purpose.
One solution would be to denormalise and always store current_owner as a model field on Book; this could be done automatically using a signal.
You can't do this. admin_order_field has to be a field, not a method - it's meant for when you have a method that returns a custom representation of an underlying field, not when you do dynamic calculations to provide the value. Django's admin uses the ORM for sorting, and that can't sort on custom methods.