I am wondering if there is a way to know how many fields a model contains.
Example:
class Post(models.Model):
title = models.TextField()
body = models.TextField()
sub_title = models.TextField()
summary = models.TextField()
I can count it but I would like to know if there is an in-built method that allows me to do so.
Ideally the quesry/code would be:
Post.number_of_fieds-->output--> 4
Does such a thing exist?
To the best of my knowledge, there is no builtin, you can obtain the fields with:
>>> len(Post._meta.fields)
5
We can define that on a BaseModel class and subclass this, to make such function available to all subclasses:
class BaseModel(models.Model):
class Meta:
abstract = True
#classmethod
def number_of_fields(cls):
return len(cls._meta.fields)
class Post(BaseModel):
title = models.TextField()
body = models.TextField()
sub_title = models.TextField()
summary = models.TextField()
The .fields return an ImmutableList of fields defined on that model. We can use .get_fields() to take into account the relations that are targetting that model as well.
Then we can query like:
>>> Post.number_of_fields()
5
Note however that this will return 5, since the model has an (implicit) primary key here.
You can try this
>>> Post._meta.fields.__len__()
5
Related
models.py
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(decimal_places=5,max_digits= 1500)
summary = models.TextField()
featured = models.BooleanField()
def __str__(self):
return self.title
# return f'product title:{self.title}-product price:{self.price}'workok
class Meta:
ordering = ('-price',)
class Opinion(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField(max_length=20)
body = models.TextField()
opinion_date = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='opinion_set')
def __str__(self):
return f'({self.name}) add opinion about ({self.product})'
forms.py:
from django.forms import ModelForm
from .models import Product #space after from keyword
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
invalid in code line :
fields = ['name','email','body','product'] #---- NOT WORK !!!
, but if i change above code to :
fields = "__all__" # ----it is WORKing ok without any problem !!
question : what is the error? I am not need all the fields in the Product model (like active boolean field), I need only 'name','email','body','product' fields .
According to the error and the code you provided the main problem is that you made a mistake in chosing model in serializer:
class OpinionModelForm(ModelForm):
class Meta:
model = Product
fields = ['name','email','body','product']
Serializer name is OpinionModelForm and listed fields belong to Opinion so I guess you actually wanted to serialize Opinion and no Product as you defined at this line:
model = Product
Simply change it to:
model = Opinion
I have a Django model used extensively in my app. I'd like to create another model that inherits from that one so I can continue using the original model throughout the code, but move a field to the new model
I have:
class MyModel(models.Model):
field1 =...
field2=...
field3=...
I want to move field3 to a new model:
class MyModel2(MyModel):
field3=...
Then I'd like MyModel instances where field3 is not null to become MyModel2 instances.
The code would continue to refer to MyModel, but in some special cases, I'd use MyModel2 instead. Is this possible? Advisable? Is there a better way? I considered making a base abstract model that both could inherit from, but then you can't use the abstract model in forms and things.
Actual model:
class Unit(models.Model):
address = models.ForeignKey(Address)
name = models.CharField(max_length=500, verbose_name="Unit Name")
payments = GenericRelation("Payment", content_type_field='content_type', object_id_field='object_pk')
permissions = GenericRelation("CustomPermission", content_type_field='content_type', object_id_field='object_pk')
association = models.ForeignKey(Association, blank=True, null=True)
def __unicode__(self):
return self.name
"association" is the field I want to move to another model.
I guess you should use abstract = True https://docs.djangoproject.com/en/1.10/topics/db/models/#abstract-base-classes
class MyModel(models.Model):
field1 =...
field2=...
field3=...
class Meta:
abstract = True
class MyModel2(MyModel):
field4=...
class AssociationBase(models.Model):
association = models.ForeignKey(Association, blank=True, null=True)
class Meta:
abstract = True
class Unit(AssociationBase):
address = models.ForeignKey(Address)
name = models.CharField(max_length=500, verbose_name="Unit Name")
payments = GenericRelation("Payment", content_type_field='content_type', object_id_field='object_pk')
permissions = GenericRelation("CustomPermission", content_type_field='content_type', object_id_field='object_pk')
def __unicode__(self):
return self.name
So, I have a data model simplified to this
class Activity(models.Model):
start_date = models.DateField()
title = models.TextField()
...
class Outcome(models.Model):
title = models.TextField()
code = models.CharField(max_length=20)
...
class ActivityOutcome(models.Model):
note = models.TextField()
outcome = models.ManyToMany(Outcome)
activity = models.ManyToMany(Activity)
class Organisation(models.Model):
title = models.TextField()
outcomes = models.ManyToManyField(Outcome, related_name='outcome_organisation')
...
class Programme(models.Model
title = models.TextField()
outcomes = models.ManyToMAnyField(Outcome, related_name='outcome_programme')
...
I want to have a single class of ActivityOutcome because it makes it much simpler to process, but I need to have some Programme Outcomes and some Organisation Outcomes. If I wanted a QuerySet of all "Programme" Outcomes, how would I do that?
You can use __isnull to filter all outcomes that have a related Programme.
outcomes = Outcome.objects.filter(outcome_programme__isnull=False)
If more than one programme can have the same outcome, you might need to use distinct() to prevent duplicates.
outcomes = Outcome.objects.filter(programme__isnull=False).distinct()
I've got a simple Django photography competition app that has three simple model definitions:
class Person(models.Model):
name = models.CharField(unique=True, max_length=255)
id = models.AutoField(primary_key=True)
class Meta:
db_table = u'people'
def __unicode__(self):
return self.name
class Round(models.Model):
theme = models.CharField(max_length=255)
number = models.IntegerField()
id = models.AutoField(primary_key=True)
class Meta:
db_table = u'rounds'
def __unicode__(self):
return self.season.name+" - "+self.theme
class Entry(models.Model):
rank = int
total_score = models.SmallIntegerField()
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, db_column='person')
round = models.ForeignKey(Round, db_column='round')
A Round has multiple Entry objects, and each Entry has one Person. One Person can obviously have multiple Entrys into different Rounds.
In the admin view, I'd like to be able to select a Round and see the details of the Entry items inline. This I can do with the following:
class EntryInline(admin.TabularInline):
model = Entry
fields = ['comments','person','total_score','image']
readonly_fields = ['person','image']
extra = 0
class RoundAdmin(admin.ModelAdmin):
fields = ['theme','number']
inlines = [EntryInline]
However, this sorts the Entry seemingly arbitrarily. I can use django's new ordering keyword in the EntryInline class to specify the ordering should be on the person, but this orders by the person Id property and not their name property.
How would I order this inline on the FK'd Person.name property?
Add this in the EntryInline class:
ordering = ["person__name"]
It is possible to do something like this working:
class Book(models.Model):
voters = models.ManyToManyField(User, blank=True)
vote = models.IntegerField() # summary of all votes
def average_vote(self):
return int(vote/self.annotate(Count('voters')))
Maybe something like this?
class Book(models.Model):
voters = models.ManyToManyField(User, blank=True)
vote = models.IntegerField() # summary of all votes
def average_vote(self):
return int(self.vote/self.voters.all().count())
Let me know if that works. I haven't tested it.
Just override the default manager to make it always return an annotated queryset:
class BookUserManager(models.Manager):
def get_query_set(self, *args, **kwargs):
return super(BookUserManager, self).get_query_set(*args, **kwargs).annotate(average_vote=models.Avg('books__vote'))
class BookUser(User):
objects = BookUserManager()
class Meta:
proxy = True
class Book(models.Model):
# Next line has been changed to use proxy model. This *will* affect the m2m table name.
voters = models.ManyToManyField(BookUser, blank=True, related_name='books')
vote = models.IntegerField() # summary of all votes
objects = BookManager()
Then, you can get at the value like any other attribute on your the user model:
user = BookUser.objects.get(username='joe')
print user.average_vote
Update: Sorry... got that all wrong. That's what I get for reading the question too quickly. You'd actually need to annotate User not Book, but since User is coming from django.contrib.auth (I'm assuming) that's not going to be possible, or at least it requires more steps. Code above has been updated.