How to use Django forms ModelChoiceField with external database values - django

I am trying to use a ModelChoiceField to get the values populated from an external database.
I have added an additional database in my setting.py and have set up a externaldb.py file in my app as follows:
from django.db import connections
def Location():
rs = []
cursor = connections['mydb'].cursor()
cursor.execute("SELECT city FROM db.data_center WHERE removed is null;")
zones = cursor.fetchall()
for v in zones[::]:
rs.append(v)
The using python manage.py shell I can do this
>>>from platform.externaldb import Location
>>>print Location()
>>>[(u'India-01',), (u'Singapore-01',), (u'Europe-01',)]
So I am getting values but how to I get that to appear in a drop down box.. This is my forms.py
forms.py
from platform.externaldb import Location
zone = forms.ModelChoiceField(Location(), label='Zone')
But this doesn't work for me.. How do I do this so the 3 values appears in the ModelChoiceField drop down list?
Thanks - Oli

You could make use of the ChoiceField form field rather then the ModelChoiceField. The problem with using a ModelChoiceField is that it expects a QuerySet. The ChoiceField allows you to add items via a List instead.
locations = forms.ChoiceField(choices=Locations(), label="Zone")
EDIT
Previously, I had used the ModelChoiceField:
locations = forms.ModelChoiceField(queryset=Location.objects.all(), label="Zone")
which will work as long as Location is a Model (which I wasn't sure of based on your code)

Related

Django models: How do I check that a decorartor property exists

I have a Profile model with a decorator property
#property
def primary_phone(self):
When I query for the primary phone for a given id like this it works
x = Profile.objects.get(id=3)
x.primary_phone
outputs
Out[3]: '+256771000022'
However filtering for the same like this
Profile.objects.filter(primary_phone="+256771000022").exists()
outputs
FieldError: Cannot resolve keyword 'primary_phone' into field. Choices are: _created_at, _modified_at, apierror, business,...)
#porperty is a managed python attribute https://docs.python.org/3/library/functions.html#property and can not be queried through DB because its data is not part of the DB.
Use a django model field if you need to query it or use Python to handle this attribute after querying the DB.

Change attribute field type in SQLite3

I am trying to change the field type of one of attributes from CharField to DecimalField by doing an empty migrations and populate the new field by filling the migrations log with the following:
from __future__ import unicode_literals
from django.db import migrations
from decimal import Decimal
def populate_new_col(apps, schema_editor): #Plug data from 'LastPrice' into 'LastPrice_v1' in the same model class 'all_ks'.
all_ks = apps.get_model('blog', 'all_ks')
for ks in all_ks.objects.all():
if float(ks.LastPrice): #Check if conversion to float type is possible...
print ks.LastPrice
ks.LastPrice_v1, created = all_ks.objects.get_or_create(LastPrice_v1=Decimal(float(ks.LastPrice)*1.0))
else: #...else insert None.
ks.LastPrice_v1, created = all_ks.objects.get_or_create(LastPrice_v1=None)
ks.save()
class Migration(migrations.Migration):
dependencies = [
('blog', '0027_auto_20190301_1600'),
]
operations = [
migrations.RunPython(populate_new_col),
]
But I kept getting an error when I tried to migrate:
TypeError: Tried to update field blog.All_ks.LastPrice_v1 with a model instance, <All_ks: All_ks object>. Use a value compatible with DecimalField.
Is there something I missed converting string to Decimal?
FYI, ‘LastPrice’ is the old attribute with CharField, and ‘LastPrice_v1’ is the new attribute with DecimalField.
all_ks.objects.get_or_create() returns an All_ks object which you assign to the DecimalField LastPrice_v1. So obviously Django complains. Why don't you assign the same ks's LastPrice?
ks.LastPrice_v1 = float(ks.LastPrice)
That said, fiddling around with manual migrations seems a lot of trouble for what you want to achieve (unless you're very familiar with migration code). If you're not, you're usually better off
creating the new field in code
migrating
populating the new field
renaming the old field
renaming the new field to the original name
removing the old field
migrating again
All steps are vanilla Django operations, with the bonus that you can revert until the very last step (nice to have when things can take unexpected turns as you've just experienced).

Change date format in Django admin page [duplicate]

I recently added a new model to my site, and I'm using an admin.py file to specify exactly how I want it to appear in the admin site. It works great, but I can't figure out how to get one of my date fields to include seconds in it's display format. I'm only seeing values like "Aug. 27, 2011, 12:12 p.m." when what I want to be seeing is "Aug. 27, 2011, 12:12*:37* p.m."
Try this in the ModelAdmin:
def time_seconds(self, obj):
return obj.timefield.strftime("%d %b %Y %H:%M:%S")
time_seconds.admin_order_field = 'timefield'
time_seconds.short_description = 'Precise Time'
list_display = ('id', 'time_seconds', )
Replacing "timefield" with the appropriate field in your model, of course, and adding any other needed fields in "list_display".
digging around I ended here but applied a different approach to my app.
Changing django admin default formats could be done changing the django locale formats for every type you want.
Put the following on your admin.py file (or settings.py) to change datetime default format at your django admin.
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT = "d M Y H:i:s"
It will change the ModelAdmin's datetime formats on that file (or whole site if in settings).
It does not breaks admin datetime filters and order features as #Alan Illing has point out in comments .
hope this help in future
Extra info:
You can change it for every available locale in django, which are a lot.
You can change the following formats using this approach
from django.conf.locale.es import formats as es_formats
es_formats.DATETIME_FORMAT
es_formats.NUMBER_GROUPING
es_formats.DATETIME_INPUT_FORMATS
es_formats.SHORT_DATETIME_FORMAT
es_formats.DATE_FORMAT
es_formats.SHORT_DATE_FORMAT
es_formats.DATE_INPUT_FORMATS
es_formats.THOUSAND_SEPARATOR
es_formats.DECIMAL_SEPARATOR
es_formats.TIME_FORMAT
es_formats.FIRST_DAY_OF_WEEK
es_formats.YEAR_MONTH_FORMAT
es_formats.MONTH_DAY_FORMAT
If you've tried gabriel's answer but it did not work, try to set USE_L10N = False in settings.py, it works for me.
Note that if USE_L10N is set to True, then the locale-dictated format has higher precedence and will be applied instead
See: https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-DATETIME_FORMAT
The accepted answer is correct, however I found it a bit confusing to understand how/why it works. Below is a small example that I hope illustrates how to do this more clearly.
Django provides a few ways to display "custom" fields in your admin view. The way I prefer to achieve this behavior is to define a custom field in the ModelAdmin class and display that instead of your intended field:
from django.contrib import admin
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
class PersonAdmin(admin.ModelAdmin):
#admin.display(description='Birthday')
def admin_birthday(self, obj):
return obj.birthday.strftime('%Y-%m-%d')
list_display = ('name', 'admin_birthday')
Notice that instead of displaying the actual birthday field from the Person model, we define a custom field (admin_birthday) as a method in the PersonAdmin and display that instead by adding it to the list_display attribute. Furthermore, the admin.display() decorator modifies how Django will display this custom field in the admin view. Using this approach, the admin panel will show the NAME and BIRTHDAY fields but using your preferred date formatting for the date.
The reason I prefer this approach is you keep the Model field definitions separate from how you display them in the admin panel. You can read more about alternative approaches in the Django admin documentation.

Indexing Taggit tags with Algolia for Django: '_TaggableManager' object has no attribute 'name'

I'm having some issues using the Algolia Django integration with one of my models which contains a TaggitManager() field. I'm currently being thrown back the following error when running this command:
$ python manage.py algolia_reindex
AttributeError: '_TaggableManager' object has no attribute 'name'
I've had a look at the Taggit documentation, but I'm just not sure exactly how I would marry the method outlined with the Algolia search index method.
index.py:
import django
django.setup()
from algoliasearch_django import AlgoliaIndex
class BlogPostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'blog_post_index'
models.py:
from taggit.managers import TaggableManager
class Post(models.Model):
...some model fields...
tags = TaggableManager()
To index the taggit tags with your Post fields, you will need to expose a callable that returns a Blog Post's tags as a list of strings.
The best option is to store them as _tags, which will let you filter on tags at query time.
Your PostIndex would look like this:
class PostIndex(AlgoliaIndex):
fields = ('title', '_tags')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
As for Post:
class Post(models.Model):
# ...some model fields...
tags = TaggableManager()
def _tags(self):
return [t.name for t in self.tags.all()]
Following these instructions, your records will be indexed with their respective tags:
You can check the taggit branch of our Django demo, which demonstrates these steps.
To answer my own question. I have now passed in both the model and the model index so Algolia now knows what to index and what not to index. Although I would like a method to allow Algolia to index taggit tags, alas, it is probably not possible.
My apps.py file:
import algoliasearch_django as algoliasearch
from django.apps import AppConfig
from .index import PostIndex
class BlogConfig(AppConfig):
name = 'blog'
def ready(self):
Post = self.get_model('Post')
algoliasearch.register(Post, PostIndex)
My index.py file:
from algoliasearch_django import AlgoliaIndex
class PostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
And that should pretty much work! Simple when you know how, or after trying about 10 different options!
So since nobody is answering I tell you how I solved this issue but I have to say that it is not a nice Way and not a "clean" Solution at all. So what I did is went into "taggit managers" in the site-packages (env->lib->python2.x/3.x-> site_packages->taggit->managers.py) In the managers.py file you will find at line 394 this beautiful piece of code:
def __get__(self, instance, model):
if instance is not None and instance.pk is None:
raise ValueError("%s objects need to have a primary key value "
"before you can access their tags." % model.__name__)
manager = self.manager(
through=self.through,
model=model,
instance=instance,
prefetch_cache_name=self.name, # this is the line I comment out when building the index,
name=self.name #this is the line I added and needs to be commented out after the index is build.
)
return manager
So what I do when I want to rebuild the search index is comment out (putting"#" infront of the line) prefetch_cache_name=self.name, and replace it with name=self.name. So building the index will work. After the Index is finished building, you have to bring everything back as it was before (switch the "#" to name=self.name again and leave prefetch_cache_name=self.name, visible again).
As already mentioned this is probably not the best way but I had the same pain and this is working for me. It takes one minute when you have the routine. Since I have to rebuild the Index maybe once every two weeks, that isn't such a deal for me but if you have to do it very often this might be annoying...
Anyway I hope that helps you.
It can help you if you using django==2+
The problem is in get_queryset() method of TaggableManager
Open file with it (my path was: Pipenv(project_name)/lib/site-packages/taggit/manager.py)
Find _TaggableManager class and change method name get_queryset to get_query_set
Done. I wish taggit's developers will fixed this in future updates

django-localflavor fields not showing up in Django admin models?

I am trying to implement django-localflavors into my Django app.
I import USStateSelect & USZipCodeField at the beginning of my models.py and then include them as a field in my model along with other fields, like so:
from localflavor.us.forms import USStateSelect, USZipCodeField
...
Class MyModel(models.Model):
...
state = USStateSelect()
zip_5 = USZipCodeField()
However, when I go to Django admin and try to create a new Model object, I see every other field I wrote (CharFields, etc.) EXCEPT any of the localflavor fields. They are simply not showing up at all as an input field in my Model object form. I have done migrations on my database so that is not the issue.
Am I misunderstanding how to use django-localflavor? I read in an answer to a different post that localflavor doesn't actually create input fields, only stores data... but then I've also read that it DOES let you input data. At this point I am confused. Any help would be appreciated!
I think what you are looking for are the model fields. The form fields are used when building your own forms (usually outside the admin, such as a contact form). Localflavor has a couple fields that should do what you need. Note that these are essentially CharFields that have some extra validation to make sure the follow the desired format.
You need to specify choices option.
Change your code a little as below:
from localflavor.us.forms import USStateSelect, USZipCodeField
...
Class MyModel(models.Model):
...
state = USStateSelect(choices=STATE_CHOICES) # add choices
zip_5 = USZipCodeField() # no change on this line