Updating Django Forms Dynamically - python

I have a simple form that provides a drop down menu of all available entries in a django Model.
forms.py
class SampleNameLookupForm(forms.Form):
#Make a list of sample name options for dropdown menu.
# Turn that into a list of tuples for ChoiceField.
samples = Sample.objects.all()
sample_list = [i.sample_name for i in samples]
sample_tuple = [(i, i) for i in sample_list]
Sample_Name = chosenforms.ChosenChoiceField(sample_tuple)
models.py
class Sample(models.Model):
sample_ID = models.CharField(max_length=20)
sample_name = models.CharField(max_length=30)
def __unicode__(self):
return self.sample_ID
class Meta:
ordering = ['id']
When I add a new sample to the model, I can see the new addition when accessing the model in mysql, or in the python manage.py shell, but it does not appear in the chosen field drop down menu.
When I restart the server, the form then reflects the new sample in the drop down menu. How can I update the drop down menu without restarting the server?
I should mention I am just locally hosting and am currently not in production.

The code needs to be inside the form's __init__ method, rather than directly in the class body, since the method is evaluated on each form instantation, rather than only when the class is defined.
class SampleNameLookupForm(forms.Form):
def __init__(self):
#Make a list of sample name options for dropdown menu.
# Turn that into a list of tuples for ChoiceField.
samples = Sample.objects.all().values_list('sample_name', flat=True)
sample_tuple = [(i, i) for i in samples]
self.fields['Sample_Name'] = chosenforms.ChosenChoiceField(sample_tuple)
Also note, I don't know what ChosenChoiceField is, but the standard Django forms library has a ModelChoiceField which is intended specifically for this use; it takes a queryset argument which is lazily evaluated, and therefore avoids the need for the __init__ stuff.

Related

ManyToMany on Self Model: How to add new instances to this relation

I have a Profile class, which extends the User class, and I would like to allow every profile to be able to bookmark other user profiles.
I've set up the following:
class Profile(models.Model):
# some fields e.g. name, email
bookmarked_profiles = models.ManyToManyField("self")
Now, I'm a bit confused, for example, how would I 'bookmark' new profiles for a given profile instance?
E.g. something like
# add new bookmark for profile instance
p = Profile.objects.get(pk=1)
profiles_to_bookmark = Profile.objects.all()
p.bookmarked_profiles = profiles_to_bookmark
I would also like to know the number of bookmarked profiles e.g.
p.bookmarked_profiles.count()
Check in django docs, there is everything that you need there
for profile in profiles_to_bookmark:
p.bookmarked_profiles.add(profile) # WIll add New objects
p.bookmarked_profiles.all() # Will return all objects
p.bookmarked_profiles.all().count() # Will return count of objects
https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_many/strong text
You can do so by adding the queryset via add() available in ManyToMnayField()
p = Profile.objects.get(pk=1)
profiles_to_bookmark = Profile.objects.all()
p.bookmarked_profiles.add(*profiles_to_bookmark) #Note the star *
And with all() you can set all bookmarked profiles, and count() will return the total
p.bookmarked_profiles.all()
p.bookmarked_profiles.count()

How to automatically fill-in model fields in Django rest_framework serializer?

Let's assume I have a model like this:
class Data(models.Model):
a = models.CharField()
b = models.CharField()
c = models.IntegerField()
I would like to setup a serializer in such a way that it automatically fills in field c and it is not required for a POST. I tried to overwrite the create function of the serializer, but it doesn't work:
class DataSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Data
fields = ('a', 'b')
def create(self, validated_data, **kwargs):
Data.objects.c = 5
return Data.objects.create(**validated_data)
However, if I try this, I end up with an IntegrityError: NOT NULL constraint failed: model_data.c. What is the syntax that I have to use here?
EDIT: Updated formatting.
The reason you're getting the error because field c is not set to null = True - as such an error is raised at the validation stage even before the serializer hits the create method.
Bear in mind that the process goes like this:
Submit serializer data
field-level validation happens - this includes checks for null integrity, min/max length etc and also any custom field validations defined in def validate_<field_name>
object-level validation happens - this calls the def validate method
validated data is passed to the save method, depending on how you designed the serializer - it will save the instance, or route the data to either create or update
All of the info regarding this can be found in Django's and DRF's docs.
A few things to consider:
are you setting a global default for that field? If so, set the default in your models - c = models.IntegerField(default=a_number_or_a_callable_that_returns_an_integer)
do you intend to display the field? If so, include c in your fields and add one more Meta attribute - read_only_fields = ('c',)
If it's neither of the above, you might want to override the validate_c method
Apologies for the poor formatting, typing it on my phone - will update once I get to a computer
In your code Data.objects.c = 5 does nothing.
If you want to set this value yourself use validated_data['c'] = 5 or Data.objects.create(c=5, **validated_data) (just not both at the same time).
Rather than doing this in the serializer, there are hooks in the generic views that allow you to pass values to the serializer. So in your case you might have:
class DataViewSet(ModelViewSet):
# ...
def perform_create(self, serializer):
serializer.save(c=5)
See the "Save and deletion hooks" section here

Django Rest Framework - Read nested data, write integer

So far I'm extremely happy with Django Rest Framework, which is why I alsmost can't believe there's such a large omission in the codebase. Hopefully someone knows of a way how to support this:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True, source='item')
item = serializers.IntegerSerializer(write_only=True)
class Meta:
model = Pin
with the goal
The goal here is to read:
{pin: item: {name: 'a', url: 'b'}}
but to write using an id
{pin: item: 10}
An alternative would be to use two serializers, but that looks like a really ugly solution:
django rest framework model serializers - read nested, write flat
Django lets you access the Item on your Pin with the item attribute, but actually stores the relationship as item_id. You can use this strategy in your serializer to get around the fact that a Python object cannot have two attributes with the same name (a problem you would encounter in your code).
The best way to do this is to use a PrimaryKeyRelatedField with a source argument. This will ensure proper validation gets done, converting "item_id": <id> to "item": <instance> during field validation (immediately before the serializer's validate call). This allows you to manipulate the full object during validate, create, and update methods. Your final code would be:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.PrimaryKeyRelatedField(write_only=True,
source='item',
queryset=Item.objects.all())
class Meta:
model = Pin
fields = ('id', 'item', 'item_id',)
Note 1: I also removed source='item' on the read-field as that was redundant.
Note 2: I actually find it rather unintuitive that Django Rest is set up such that a Pin serializer without an Item serializer specified returns the item_id as "item": <id> and not "item_id": <id>, but that is beside the point.
This method can even be used with forward and reverse "Many" relationships. For example, you can use an array of pin_ids to set all the Pins on an Item with the following code:
class ItemSerializer(serializers.ModelSerializer):
pins = PinSerializer(many=True, read_only=True)
pin_ids = serializers.PrimaryKeyRelatedField(many=True,
write_only=True,
source='pins',
queryset=Pin.objects.all())
class Meta:
model = Item
fields = ('id', 'pins', 'pin_ids',)
Another strategy that I previously recommended is to use an IntegerField to directly set the item_id. Assuming you are using a OneToOneField or ForeignKey to relate your Pin to your Item, you can set item_id to an integer without using the item field at all. This weakens the validation and can result in DB-level errors from constraints being violated. If you want to skip the validation DB call, have a specific need for the ID instead of the object in your validate/create/update code, or need simultaneously writable fields with the same source, this may be better, but I wouldn't recommend anymore. The full line would be:
item_id = serializers.IntegerField(write_only=True)
If you are using DRF 3.0 you can implement the new to_internal_value method to override the item field to change it to a PrimaryKeyRelatedField to allow the flat writes. The to_internal_value takes unvalidated incoming data as input and should return the validated data that will be made available as serializer.validated_data. See the docs: http://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data
So in your case it would be:
class ItemSerializer(ModelSerializer):
class Meta:
model = Item
class PinSerializer(ModelSerializer):
item = ItemSerializer()
# override the nested item field to PrimareKeyRelatedField on writes
def to_internal_value(self, data):
self.fields['item'] = serializers.PrimaryKeyRelatedField(queryset=Item.objects.all())
return super(PinSerializer, self).to_internal_value(data)
class Meta:
model = Pin
Two things to note: The browsable web api will still think that writes will be nested. I'm not sure how to fix that but I only using the web interface for debug so not a big deal. Also, after you write the item returned will have flat item instead of the nested one. To fix that you can add this code to force the reads to use the Item serializer always.
def to_representation(self, obj):
self.fields['item'] = ItemSerializer()
return super(PinSerializer, self).to_representation(obj)
I got the idea from this from Anton Dmitrievsky's answer here: DRF: Simple foreign key assignment with nested serializers?
You can create a Customized Serializer Field (http://www.django-rest-framework.org/api-guide/fields)
The example took from the link:
class ColourField(serializers.WritableField):
"""
Color objects are serialized into "rgb(#, #, #)" notation.
"""
def to_native(self, obj):
return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
def from_native(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
Then use this field in your serializer class.
I create a Field type that tries to solve the problem of the Data Save requests with its ForeignKey in Integer, and the requests to read data with nested data
This is the class:
class NestedRelatedField(serializers.PrimaryKeyRelatedField):
"""
Model identical to PrimaryKeyRelatedField but its
representation will be nested and its input will
be a primary key.
"""
def __init__(self, **kwargs):
self.pk_field = kwargs.pop('pk_field', None)
self.model = kwargs.pop('model', None)
self.serializer_class = kwargs.pop('serializer_class', None)
super().__init__(**kwargs)
def to_representation(self, data):
pk = super(NestedRelatedField, self).to_representation(data)
try:
return self.serializer_class(self.model.objects.get(pk=pk)).data
except self.model.DoesNotExist:
return None
def to_internal_value(self, data):
return serializers.PrimaryKeyRelatedField.to_internal_value(self, data)
And so it would be used:
class PostModelSerializer(serializers.ModelSerializer):
message = NestedRelatedField(
queryset=MessagePrefix.objects.all(),
model=MessagePrefix,
serializer_class=MessagePrefixModelSerializer
)
I hope this helps you.

Can model views in Flask-Admin hyperlink to other model views?

Let's suppose we have a model, Foo, that references another model, User - and there are Flask-Admin's ModelView for both.
On the Foo admin view page
I would like the entries in the User column to be linked to the corresponding User model view.
Do I need to modify one of Flask-Admin's templates to achieve this?
(This is possible in the Django admin interface by simply outputting HTML for a given field and setting allow_tags (ref) True to bypass Django's HTML tag filter)
Some example code based on Joes' answer:
class MyFooView(ModelView):
def _user_formatter(view, context, model, name):
return Markup(
u"<a href='%s'>%s</a>" % (
url_for('user.edit_view', id=model.user.id),
model.user
)
) if model.user else u""
column_formatters = {
'user': _user_formatter
}
Use column_formatters for this: https://flask-admin.readthedocs.org/en/latest/api/mod_model/#flask.ext.admin.model.BaseModelView.column_formatters
Idea is pretty simple: for a field that you want to display as hyperlink, either generate a HTML string and wrap it with Jinja2 Markup class (so it won't be escaped in templates) or use macro helper: https://github.com/mrjoes/flask-admin/blob/master/flask_admin/model/template.py
Macro helper allows you to use custom Jinja2 macros in overridden template, which moves presentational logic to templates.
As far as URL is concerned, all you need is to find endpoint name generated (or provided) for the User model and do url_for('userview.edit_view', id=model.id) to generate the link.
extra information for #wodow, notice that model.user is wrong if you use pymongo as the backend, because the model in pymongo is a dict type, you can just use model['name'] to replace it
Adding this code to each of your models that have referenced by other models and flask-admin and jinja will take care of the name you want to display on the screen, just replace that with whatever you prefer:
def __unicode__(self):
return self.name # or self.id or whatever you prefer
for example:
class Role(db.Document, RoleMixin):
name = db.StringField(max_length=80, unique=True)
description = db.StringField(max_length=255)
def __unicode__(self):
return self.name
class MasterUser(db.Document, UserMixin):
email = db.StringField(max_length=255)
password = db.StringField(max_length=255)
active = db.BooleanField(default=True)
confirmed_at = db.DateTimeField()
roles = db.ListField(db.ReferenceField(Role), default=[])

Way to allow for duplicate many-to-many entries in Python/Django

I have the following Django model:
class Icon(models.Model):
name = models.CharField(max_length=200,null=False,blank=False)
class Post(models.Model):
icons = models.ManyToManyField(Icon)
When I write the following code:
post = Post()
icons = []
icon_id = form.cleaned_data['icon_1']
if (icon_id):
i = Icon.objects.get(id=icon_id)
icons.append(i)
icon_id = form.cleaned_data['icon_2']
if (icon_id):
i = Icon.objects.get(id=icon_id)
icons.append(i)
post.icons = icons
post.save()
It works fine for the most part, creating a Post object and the two Icon objects.
However, if the icon_id is, say, 1 in both cases, it only creates ONE entry in the database, not two.
So it seems like it checks for duplicates and removes them.
How do I make this work so I allow duplicates? (I want two of the SAME icon associated with a post.)
Thanks!
Define the model yourself, to have such non-unique many-to-many relations
class PostIcon(models.Model):
post = models.ForeignKey(Post)
icon = models.ForeignKey(Icon)
and than add them one by one
for icon in icons:
PostIcon(post=post, icon=icon).save()
or pass that model as through argument of ManyToManyField e.g.
class Post(models.Model):
icons = models.ManyToManyField(Icon, through=PostIcon)
or alternatively you can have a count associated with PostIcon instead of having multiple rows, if that serves the use-case e.g. you may want a badge to be displayed 10 times
class PostIcon(models.Model):
post = models.ForeignKey(Post)
icon = models.ForeignKey(Icon)
count = models.IntegerField()

Categories