django-grappelli Autocomplete Lookups with multiple foreign key fields - django-grappelli

I have a model with two fields that are foreign keys to other models.
class Homepage(models.Model):
featured_user = models.ForeignKey('auth.user')
featured_story = models.ForeignKey('site_stories.story')
#staticmethod
def autocomplete_search_fields():
return ("featured_user__icontains", "featured_story__icontains",) # Is this right?
class HomepageAdmin(admin.ModelAdmin):
raw_id_fields = ('featured_user', 'featured_story',)
autocomplete_lookup_fields = {
'fk': ['featured_user'],
'fk': ['featured_story'] # <====== What should this be???
}
admin.site.register(Homepage, HomepageAdmin)
After reading the admin docs and trying a few things, it became clear that you literally need to use the label "fk" for grappelli to apply the autocomplete lookup formatting to a field. So... how can I do this with this model, where there are multiple foreign key fields?

class HomepageAdmin(admin.ModelAdmin):
raw_id_fields = ('featured_user', 'featured_story',)
autocomplete_lookup_fields = {
'fk': ['featured_user','featured_story'],
}

Related

Django: constraint with nullable multiple field conditions

I have a model in Django/PostgreSQL project and want to add following constraints to three nullable fields: either all fields are NULL, either all of them aren't.
Here's code (simplified):
class SomeModel(models.Model):
...
a = models.IntegerField(nullable=True)
b = models.DateTimeField(nullable=True)
c = models.DateTimeField(nullable=True)
...
class Meta:
constraints = [
models.CheckConstraint(
check=(
(Q(a__isnull=True) & Q(b__isnull=True) & Q(c__isnull=True)) |
(Q(a__isnull=False) & Q(b__isnull=False) & Q(c__isnull=False))
)
)
]
If I understand correctly, I've just described two possible states of those three fields. First is "all three are NULL", second is "none of them are NULL".
But what I got, actually, is "none of them can be NULL". Django admin panel insists on filling all of the fields, they all are mandatory. How can I fix this behaviour? Thanks!
This is not due to the constraints, you should specify blank=True [Django-doc] when you want to allow to leave form fields blank, and thus default to None/NULL:
class SomeModel(models.Model):
# …
a = models.IntegerField(null=True, blank=True)
b = models.DateTimeField(null=True, blank=True)
c = models.DateTimeField(null=True, blank=True)
class Meta:
constraints = [
models.CheckConstraint(
check=Q(a=None, b=None, c=None) |
Q(a__isnull=False, b__isnull=False, c__isnull=False),
name='all_or_none_null'
)
]

Writeable Nested Serializers with PrimaryKeyRelatedField

I'm having a problem creating (nested) serializers for the following models:
class Chapter(Model):
uuid = UUIDField(primary_key=True, editable=False)
description = TextField(blank=True)
class Section(Model):
uuid = UUIDField(primary_key=True, editable=False)
description = TextField(blank=True)
chapter = ForeignKey(Chapter, related_name='sections', on_delete=CASCADE)
Here is the serializer for the Chapter model with the create method:
class ChapterSerializer(ModelSerializer):
uuid = UUIDField(read_only=False)
sections = SectionSerializer(many=True, read_only=False)
class Meta:
model = Chapter
fields = ('uuid', 'description', 'sections')
def create(self, validated_data):
sections = validated_data.pop('sections', [])
chapter = Chapter.objects.create(**validated_data)
for section in sections:
section.update({'chapter': chapter})
section = Section.objects.create(**section)
return chapter
And here are two slightly different variants of the Section serializer:
class SectionSerializer(ModelSerializer):
uuid = UUIDField(read_only=False)
chapter = PrimaryKeyRelatedField(read_only=true)
class Meta:
model = Section
fields = ('uuid', 'description', 'chapter')
class SectionSerializer(ModelSerializer):
uuid = UUIDField(read_only=False)
chapter = PrimaryKeyRelatedField(queryset=Chapter.objects.all())
class Meta:
model = Section
fields = ('uuid', 'description', 'chapter')
Now if I try to create a new Chapter with nested Sections and the PrimaryKeyRelatedField in the Section serializer has the queryset parameter set, I get the following error:
'sections': [{'chapter': ['Invalid pk "x" - object does not exist.']}]
If I use the variant with the read_only=true parameter, creating a new Chapter with nested Sections works, but now I can no longer create a new Section (Chapter field is set to an existing Chapter UUID) because the chapter field is removed while validating the input data.
django.db.utils.IntegrityError: null value in column "chapter_id" violates not-null constraint
I think I could solve this by duplicating the SectionSerializer, but this seems to be a very crude solution ...
Anyone knowing of an better approach?
OK, answering my own question:
I finally found a way to get a "nestable" and a "writeable" version of the SectionSerializer without code duplication:
class NestedSectionSerializer(ModelSerializer):
uuid = UUIDField(read_only=False)
class Meta:
model = Section
exclude = ('chapter', )
class SectionSerializer(NestedSectionSerializer):
url = serializers.HyperlinkedIdentityField(view_name='section-detail')
class Meta:
model = Section
fields = '__all__'
class ChapterSerializer(ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='chapter-detail')
uuid = UUIDField(read_only=False)
sections = NestedSectionSerializer(many=True, read_only=False)
class Meta:
model = Chapter
fields = '__all__'
def create(self, validated_data):
sections = validated_data.pop('sections', [])
chapter = Chapter.objects.create(**validated_data)
for section in sections:
section.update({'chapter': chapter})
section = Section.objects.create(**section)
return chapter
The key here ist to explicitly exclude the reverence to chapter in the "nested" serializer but add it again in the "writeable" serializer. This even scales to deeply nested structures!
And by using inheritance I can avoid most code duplication.
I just pulled my hair about this very same question.
The answer is actually in the DRF documentation: you need to explicitely define the queryset argument for the PrimaryKeyRelatedField if you want them to be writable.
In your case, only the second variant of SectionSerializer is necessary.

Use a model's field as an identifier

I have the following model in OpenERP 7. How to indicate that the isbn field should be the primary key.
from osv import osv, fields
class Book(osv.Model):
""" A book """
_name = 'helloworld.book'
_columns = {
'isbn' : fields.char('ISBN', size=9, requried=True),
'title' : fields.char('Title', size=100, required=True),
'genre' : fields.char('Genre', size=20, required=True),
}
A primary key is a special relational database table column (or combination of columns) designated to uniquely identify all table records.
A primary key’s main features are:
It must contain a unique value for each row of data.
It cannot contain null values.
A primary key is either an existing table column or a column that is specifically generated by the database according to a defined sequence.
OpenERP Supports it by Keeping unique sql constraint on field.
_sql_constraints = [
('my_key_value', 'unique(key_value1,key_value2)', 'key value has to be unique !')
]
In your case, As you have chosen Char type of Field, Set sql constraint like this.Add this to your class. and restart the server and check again. You will find primary key's functionality on 'isbn' field.
_sql_constraints = [
('isbn_uniq', 'unique(isbn)', 'ISBN must be unique!'),
]
For case insensitive constraints,
def _check_unique_insesitive(self, cr, uid, ids, context=None):
sr_ids = self.search(cr, uid ,[], context=context)
lst = [x.ibsn.lower() for x in self.browse(cr, uid, sr_ids, context=context) if x.ibsn]
for self_obj in self.browse(cr, uid, ids, context=context):
if self_obj.ibsn and self_obj.ibsn.lower() in lst:
return False
return True
_constraints = [(_check_unique_insesitive, 'Error: ISBN must be unique!', ['ibsn'])]
Hope this will help you. :)

Preserve Order of IN in ORM Order

I'm trying to do a query where I preserve the order of the ids in a IN statement. I can't seem to do it with either the Model Manage Query Builder or the standard ORM 'order' array parameter. Am I missing something? I keep getting:
UNEXPECTED TOKEN IDENTIFIER(, NEAR TO 'id`enter code here`,17743,16688,16650
Here's my model manager:
$query = $this->modelsManager->createQuery('SELECT * FROM Projects WHERE id IN ('.implode(',', array_keys($finalIterations)).')
ORDER BY FIELD(id,'.implode(',', array_keys($finalIterations)).'');
It's pretty obvious PhQL doesn't like the FIELD key word. Is there a way for me to do what I'm trying to do with PhQL? It seems I will not be able to do what I need to.
Unfortunately as previously said, this is missing a feature in Phalcon.
Have a look at this function, I've put it into my ModelBase abstract class which is parent class of all my models. It uses PhQL variable binding, so it's safe for handling direct user input.
You could have reimplemented custom \Phalcon\Mvc\Model\Criteria but this solution seems to be easier to work with, at least for me.
ModelBase abstract
public function appendCustomOrder( \Phalcon\Mvc\Model\CriteriaInterface &$criteria, $orderField, array &$orderValues = [] ) {
if(!empty($orderValues)) {
$queryKeys = $bindParams = [];
foreach($orderValues as $key => $id) {
$queryKey = 'pho'.$key;
$queryKeys[] = ':'.$queryKey.':';
$bindParams[$queryKey] = $id;
}
// TODO: add support for multiple orderBy fields
$criteria->orderBy('FIELD('.$orderField.','.implode(',',$queryKeys).')');
// there's no 'addBind' function, need to merge old parameters with new ones
$criteria->bind( array_merge( (array) #$criteria->getParams()['bind'], $bindParams ) );
}
}
Controller usage
$projectIDs = [17743, 16688, 16650];
$projectsModel = new Projects();
$criteria = $projectsModel->query->inWhere( 'id', $projectIDs );
$projectsModel->appendCustomOrder( $criteria, 'id', $projectIDs );
$projectsData = $criteria->execute();
This will generate valid PhQL syntax similar to this one:
SELECT `projects`.`id` AS `id`, `projects`.`title` AS `title`
FROM `projects`
WHERE `projects`.`id` IN (:phi0, :phi1, :phi2)
ORDER BY FIELD(`projects`.`id`, :pho0, :pho1, :pho2)

how to create an index in mongoengine to be unique=True and sparse=True

I am using mongoengine with flask. I have a db.Document class called profile in which i want a field to be nullable and unique, i understand the way to do this is to make an index of that field that is both sparse=True and unique=True, how do i go about doing this?
You will have to declare the index in the meta definition eg:
class BlogPost(Document):
date = DateTimeField(db_field='addDate', default=datetime.now)
category = StringField()
tags = ListField(StringField())
meta = {
'indexes': [
{'fields': ['-date'], 'unique': True,
'sparse': True, 'types': False },
],
}
In case of unique constraint you can set it with the field declaration as:
email = mongodb.EmailField(required=True, unique=True)
We can directly mention it in the Field parameters. Example:
email = db.EmailField(sparse=True, unique=True, required=False)