how to make django's choice option dynamic in the model - python

I am creating a reference lexicon in django. The lexicon is a translator for certain fields from one language to another.
The lexicon goes like this: 'id','lng1','lng2','lng3'
I have a model that uses the lexicon in one of the fields.
class lexicon(models.Model):
lng1 = model.Charfield(max_length=255)
lng2 = model.Charfield(max_length=255)
lng3 = model.Charfield(max_length=255)
lexicon_choices = (
(lng1=lng1),
(lng2=lng2),
(lng3=lng3)
)
class model(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = model.Charfield(max_length=3,CHOICES=lexicon_choices)
This works ok, but I want to know, is there a way to glean out the column names from class lexicon and port them as the choices for class model? I am using Django Rest Framework for the first time, and I want to make sure the right lng is there.
Thanks

You can use a ForeignKey on the lexicon class to your main model. This will ensure a One to Many relationship. And you can store the values in database.

yes, possible. but in admin.py
class CustomForm(forms.ModelForm):
class Meta:
model = YourModel
def __init__(self, *args, **kwargs):
super(CustomForm, self).__init__(*args, **kwargs)
choices = [(x.id, x.name) for x in Lexicon.objects.all()]
self.fields['field1'].choices = choices
class YourModelAdmin(admin.ModelAdmin):
form = CustomForm
and your original model looks like:
class YourModel(models:Model):
model_name = model.Charfield(max_length=255, unique)
field1 = models.CharField(max_length=10, choices=(('--', '--'),))
IMPORTANT: do not name your class names with lower case. ;)

Related

Django Access to Foreign Key data to set a field default value

I have two models with their respective forms. One has a Foreign Key link to the other and from, here I would like to set some fields default data.
class Lexicon(models.Model):
[...]
case_sensitive = models.BooleanField(default=True)
invariant = models.NullBooleanField(default=False)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexicon"
ordering = ["filename"]
def __str__(self):
return self.filename
class Lexeme(models.Model):
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
[...]
class Meta:
verbose_name = "lexeme"
I would like the Lexeme model fields "case_sensitive" and "diacritics" to default from Lexicon. I suppose the forms may be a better place to do this.
Any idea ?
As I understand, you only need to populate data from Lexicon to Lexeme model fields. You can override get_form_kwargs in your FormView as follows
def get_form_kwargs(self):
lex_obj = Lexeme.objects.get(pk=self.kwargs['pk'])
kwargs = super().get_form_kwargs()
kwargs['initial']['case_sensitive'] = lex_obj.lexicon.case_sensitive
kwargs['initial']['diacritics'] = lex_obj.lexicon.diacritics
return kwargs
Is that what you want? I have not tested but, I have used similar thing on my project. Let me know if works or not.
I finally found the way to go. It was just basic initial setting of field, no need to touch to forms.py, models.py nor the html template.
I passed data to my form like this:
lexeme_form = LexemeForm(initial={'case_sensitive': lexicon.case_sensitive, 'diacritics': lexicon.diacritics})
use Ajax at template to change the initial value of "case_sensitive" and "diacritics" when Lexicon changed, and abstract model can be used to reduce repeat lines :
class BaseLex(models.Model):
case_sensitive = models.BooleanField(default=True)
diacritics = models.BooleanField(default=True)
class Meta:
abstract = True
class Lexicon(BaseLex):
# without `case_sensitive` and `diacritics' fields
...
class Lexeme(BaseLex):
# without `case_sensitive` and `diacritics' fields
lexicon = models.ForeignKey(Lexicon, on_delete=models.CASCADE)
...

How to specify the database for Factory Boy?

FactoryBoy seem to always create the instances in the default database. But I have the following problem.
cpses = CanonPerson.objects.filter(persons__vpd=6,
persons__country="United States").using("global")
The code is pointing to the global database. I haven't found a way to specify the database within the factory:
class CanonPersonFactory(django_factory.DjangoModelFactory):
class Meta:
model = CanonPerson
django_get_or_create = ('name_first', 'p_id')
p_id = 1
name_first = factory.Sequence(lambda n: "name_first #%s" % n)
#factory.post_generation
def persons(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of groups were passed in, use them
for person in extracted:
self.persons.add(person)
Looks like Factory Boy does not provide this feature from box, but you can easily add it manually:
class CanonPersonFactory(django_factory.DjangoModelFactory):
class Meta:
model = CanonPerson
...
#classmethod
def _get_manager(cls, model_class):
manager = super(CanonPersonFactory, cls)._get_manager(model_class)
return manager.using('global')
...
This is now directly supported by adding the database attribute on Meta:
class CanonPersonFactory(django_factory.DjangoModelFactory):
class Meta:
model = CanonPerson
database = 'global'
...

How use MethodFilter from django-filter app?

I don't know how to use MethodFilter in django-filter app and I can't find it in the documentation (http://django-filter.readthedocs.org/en/latest/index.html). I need create filter with from and to input for date field. Do you have any idea how I can do this with MethodFilter or another way?
I have some class:
class Product(django_filters.FilterSet):
class Meta:
model = Product
fields = ['name', 'category', 'created_at]
I have field created_at and I want to filter products by created_at (from and to).
To answer the How to use MethodFilter part of the question, define a method on your FilterSet and assign that as the filter's action.
For example to filter by a username you would do something such as:
class F(FilterSet):
username = MethodFilter(action='filter_username')
class Meta:
model = User
fields = ['username']
def filter_username(self, queryset, value):
return queryset.filter(
username=value
)
I'm not sure I understand fully what you're asking, but to filter your products you can use Q
https://docs.djangoproject.com/en/1.7/topics/db/queries/#complex-lookups-with-q
from django.db.models import Q
queryset = Products.objects.filter(Q(created_at__gte=from)&Q(created_at__ite=to)
I searched in GitHub and I found this. It works.
class DateRangeField(django_filters.fields.RangeField):
# Django-Filter DateRangeFilter that really accepts a range of dates ;)
def __init__(self, *args, **kwargs):
fields = (
forms.DateField(),
forms.DateField(),
)
forms.MultiValueField.__init__(self, fields, *args, **kwargs)
class DateRangeFilter(django_filters.RangeFilter):
field_class = DateRangeField

Django.rest_framework: How to serialize one to many to many?

I have some troubles serializing with django.
I have three models, let's say a School, a Room and a Desk (dummy name for example).
Each School have multiple Room, and each Room have multiple Desk.
The classes and their relations look like this :
class School(models.Model):
name = models.CharField()
class Room(models.Model):
name = models.CharField()
school_id = models.ForeignKey(School)
class Desk(models.Model):
row = models.IntegerField()
col = models.IntegerField()
room_id = models.ForeignKey(Room)
I want to be able to serialize a list of School, each directly containing all the desks inside.
The closet I got was by writing in my serialize.py three serializer :
class DeskSerializer(serializers.ModelSerializer):
class Meta:
field = (row, col,)
class RoomSerializer(serializers.ModelSerializer):
desks = DeskSerializer(source='desk_set', many=True)
class Meta:
field = (desks,)
class SchoolSerializer(serializers.ModelSerializer):
rooms = RoomSerializer(source='room_set', many=True)
class Meta:
field = (name, rooms,)
Which return a list of school containing a list of room containing a list of desk, when I want a list of School containing a list of desk
Which source should I use in the School serializer to return directly the desk? Something like source='room_set.desk_set'? Or maybe by using a transform_ function?
PS: the code is write from scratch on the post, please ignore the possible syntax errors
If I'm understanding you correctly, you want the SchoolSerializer to return a nested structure 2 levels deep, but skipping the intermediate model. To do this, I would create a method in your School model to retrieve the Desk instances:
class School(models.Model):
...
def get_desks(self):
rooms = Room.objects.filter(school=self)
desks = Desk.objects.filter(room__in=rooms)
return desks
Then, in your SchoolSerializer include a field that uses that method and renders the returned instances as you wish via your DeskSerializer:
class SchoolSerializer(serializers.ModelSerializer):
...
desks = DeskSerializer(
source='get_desks',
read_only=True
)
class Meta:
field = (name, desks)
The key to understanding how this works is that the model method used as the value for the source argument must simply return instances of that serializer's model. The serializer takes it from there, rendering the object(s) however you defined them within that serializer (in this case the DeskSerializer).
Hope this helps.

Django admin inline form change choice field to charfield dynamically

I want to change formfield widget depend on other field value. Default is select because model field is foreign key. My models are as follows:
class ProductFeatureValue(BaseName):
feature = models.ForeignKey('ProductTypeFeature')
class Meta:
verbose_name = _(u'Product Feature Value')
verbose_name_plural = _(u'Product Feature Values')
class ProductFeature(models.Model):
product = models.ForeignKey('Product')
feature = models.ForeignKey('ProductTypeFeature')
value = models.ForeignKey('ProductFeatureValue')
And my form is as follows:
class ProductFeatureFormForInline(ModelForm):
class Meta:
model = ProductFeature
def __init__(self,*args,**kwargs):
super(ProductFeatureFormForInline,self).__init__(*args,**kwargs)
if isinstance(self.instance,ProductFeature):
try:
widget_type = self.instance.feature.product_type.producttypefeature_set.all()[0].widget #TODO Fix that 0 slice
if widget_type == u'TEXT':
self.fields['value'] = CharField(widget=TextInput())
if widget_type == u'MULTIPLE_SELECT':
self.fields['value'].widget = MultipleChoiceField()
except:
pass
It changes the fields widget but when it make it charfield and populate it with instance it shows the id of the model not the value (author : 1) and it makes sense to show it that way, but i want to show(author: Dan Brown).
I have tried with initial values but not working. Any tips of doing that will be highly appreciated. Thanks
Your __unicode__() method on the model should dictate what is shown there, if I'm not missing something.
On your model:
class ProductFeatureValue(BaseName):
[... snipped code ...]
def __unicode__():
return self.feature
This snippet assumes that self.feature is what you want to return, and not something else on the parent BaseName.

Categories

Resources