I'm still a novice Django/Python user, so apologies for the basic question.
I'm running into a problem where maps are not showing up in my Django admin. I was hoping to fix this problem by adding the OpenLayersWidget class to my forms.py project file, referenced in the Django docs here: https://docs.djangoproject.com/en/1.9/ref/contrib/gis/forms-api/#django.contrib.gis.widgets.OpenLayersWidget. See below.
from django import forms
from django.contrib.gis.admin.widgets import OpenLayersWidget
class OpenLayersWidget(forms.TextInput):
"""Specifying CDN of openlayers.js in the Media class."""
class Media:
js = (
'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1',
)
I'm sure this is too simplistic.
Unfortunately, the above does not work over https. Any ideas?
Try this:
from django.contrib.gis import admin as gis_admin
class SecureOSM(gis_admin.OSMGeoAdmin):
openlayers_url = 'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js'
Then inherit from here
Update
The https cloudflare url has become the default as of Django 1.11. So patching should not be necessary anymore.
You need override the forms Widget. Example:
from django.contrib import admin
from django.contrib.gis.db import models
from django.contrib.gis.forms.widgets import BaseGeometryWidget
class CustomOpenLayersWidget(BaseGeometryWidget):
template_name = 'gis/openlayers.html'
def __init__(self, attrs=None):
super(CustomOpenLayersWidget, self).__init__(attrs)
class Media:
js = (
'https://cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js',
'gis/js/OLMapWidget.js',
)
class CustomModelAdmin(admin.ModelAdmin):
"""Need to change default URL of OpenLayers"""
formfield_overrides = {
models.PointField: {"widget": CustomOpenLayersWidget},
models.PointField: {"widget": CustomOpenLayersWidget},
models.PolygonField: {"widget": CustomOpenLayersWidget},
models.LineStringField: {"widget": CustomOpenLayersWidget},
# Adding other models Fields if need
}
and change the admin class
#admin.register(Position)
class PositionAdmin(CustomModelAdmin):
pass
A example of model
class Position(models.Model):
point = models.PointField(blank=False)
The code override field forms widgets to a new media itens. This remove http content http://openlayers.org/api/2.13.1/OpenLayers.js of media.
Related
I am trying to extend Django admin model.
This is the code that I have:
from django.contrib import admin
from .models import *
from markdownx.admin import MarkdownxModelAdmin
def process_topics(_, __, queryset):
from person.tasks import calculate_factors
for object in queryset:
calculate_factors(object)
process_topics.short_description = "Process topics manually"
class ObjectAdmin(admin.ModelAdmin):
list_display = ('pk', 'sender', 'note',)
actions = [process_topics, ]
# admin.site.register(Object, ObjectAdmin)
admin.site.register(Object, MarkdownxModelAdmin)
I would like to use ObjectAdmin to extend MarkdownxModelAdmin class.
Posting as an answer, since there is no accepted answer yet :)
class ObjectAdmin(MarkdownxModelAdmin): ... Should do it.
I have two Django models.py in two different apps.
processor app models.py
from address.models import Address
...defining clasess
class Displayble(models.Model):
# It has no DB fields
address app models.py
from processor.models import Displayable
class Address(models.Model, Displayble):
...some fields, stored in DB
Is moving Dispalyble class to another file is the only option to resolve this dependency?
Import the Address model with django's apps.get_model. https://docs.djangoproject.com/en/1.11/ref/applications/#django.apps.apps.get_model.
In your processor app models.py replace
from address.models import Address
...defining clasess
class Displayble(models.Model):
# It has no DB fields
With
from django.apps import apps
Address = apps.get_model(app_label='address', model_name='Address')
....go ahead and use Address as though imported
class Displayable(models.Model):
...
I have a model class like this
models.py
class Places(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=50)
tel = models.CharField(max_length=50)
I want to put a google map under the address field and have a custom button next to the address field. The button is used to pin point the location when user click on it, based on the address input.
So, my question is how do I insert the button next to the address fields and html tags under it for map rendering?
Thanks.
You'll have to write a custom widget.
Take a look at this, this and django documentation.
You'll probably need to write south introspection rules
There is a django-easy-maps module that can do exactly what you need (besides other features):
django-easy-maps provides basic widget that displays a map under the
address field. It can be used in admin for map previews.
Note that you would not need to change your model and write any specific migrations. It works with either CharField, or TextField out of the box.
Follow the installation instructions and add the following to the admin.py of your app:
from django import forms
from django.contrib import admin
from easy_maps.widgets import AddressWithMapWidget
from my_app.models import Places
class PlacesAdmin(admin.ModelAdmin):
class form(forms.ModelForm):
class Meta:
widgets = {
'address': AddressWithMapWidget({'class': 'vTextField'})
}
admin.site.register(Places, PlacesAdmin)
I end up refer to this tutorial to create a custom widget.
For anyone who are interested, here are my codes
widgets.py
class AddressFieldWidget(forms.TextInput):
def render(self, name, value, attrs=None):
html = super(AddressFieldWidget, self).render(name, value,
attrs)
html = html + """ <input type="button" value="GeoCode" class="getgeo btn"><br><br><label>Map</label><div id="gmap">This is for map rendering</div>"""
return mark_safe(html)
admin.py
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'address':
kwargs['widget'] = AddressFieldWidget
return super(AdminListing,self).formfield_for_dbfield(db_field,**kwargs)
in my new Django app I use 3rd-part Photologue app.
This app works well, but I need to add some field in its model without change the original source code of Photologue app.
With this snippet, all works well, but maybe this is not the best solution:
Photo.add_to_class("link", models.URLField(max_leng.... ))
I've read some posts where I should use OneToOneRelationField ... but then, in django admin app, I will have two different form page of Photo and new PhotoLink class...
Does anyone can help me?
If you are using a OneToOneField, you can use your 'extension' model as an inline to the model you are extending. You do this by first un-registering the original admin class, and then overriding it with our variant:
from otherapp.admin import OriginalFooAdmin
from otherapp.models import Foo
from yourapp.models import YourExtension
from django.contrib import admin
admin.site.unregister(Foo)
class ExtensionInline(admin.StackedInline):
model = YourExtension
class NewFooAdmin(OriginalFooAdmin):
inlines = OriginalFooAdmin.inlines + [ExtensionInline]
admin.site.register(Foo, NewFooAdmin)
I wrote an app that is mainly allowing the user to drag tags to objects via jQuery. I want to allow that app to work for multiple models, so that i can tag ie. a user or an image. For this i thought about adding a class containing a "dropcode" to each models representation on the page:
<div class="droppable" dropcode="drop_img"> some image </div>
<div class="droppable" dropcode="drop_user"> some user </div>
I would like to specify the "dropcode" for each of the models in the main projects settings:
droppable_models={User:'drop_user',Image:'drop_img'}
After installing the app, i want to be able to retrieve the dropcode from each instance of the affected models:
image_instance1.dropcode -> drop_img
image_instance2.dropcode -> drop_img
user_instance1.dropcode -> drop_user
user_instance2.dropcode -> drop_user
That way i could just simply use the dropcode on the page, return it via jQuery to select the right model
Is that possible? Is there a better way to achieve what i want do do?
Why not add a dropcode property to the appropriate models? eg.
class Image(models.Model):
....
dropcode = property(lambda self: "drop_img")
For existing models where you can't edit the models.py (such as User model), add code like this to the models.py of one of your own apps:
from django.contrib.auth.models import User
class UserMixin:
dropcode = property(lambda self: "drop_user")
User.__bases__ += (UserMixin,)
Then in your template, use an if tag to check whether an item has a dropcode. You can therefore eliminate the droppable_models setting:
<div class="droppable"{% if item.dropcode %} dropcode="{{item.dropcode}}"{% endif %}>{{item}}</div>
If your application should work with any model, then you should use the contentypes framework:
Django includes a contenttypes application that can track all of the
models installed in your Django-powered project, providing a
high-level, generic interface for working with your models.
Implementing this allows your application to be generic - it can work with any installed model.
EDIT:
Here is how to use content types framework (directly from the documentation):
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag
Now, to add a tag to the item:
item = Model.object.get(pk=1)
ti = TaggedItem(tag='foo',content_object=item)
ti.save()
To get tags for a particular item:
i = Image.object.get(pk=1) # or any instance of the Image object, or any object
the_type_of_object = ContentType.objects.get_for_model(i)
# Find all tags for this object
image_tags = TaggedItem.objects.filter(content_type__pk=the_type_of_object.id,
object_id=i.id)
Based on the tips of Simon and Burhan i came to the following solution: I define the affected models in the settings and then add the DragToTagable Class as Base Class to those models. This look like that in the settings:
DROPPABLE_MODELS=('test.TestItem:item',)
Thats all that needs to be done to apply the apps functionality to that model, or any other model of my project. The model.py of my app looks like this now:
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
try:
#perform this when starting the project
for definition in settings.DROPPABLE_MODELS:
#parse contenttype
parsed=definition.split(':')
dropcode=parsed[1]
parsed=parsed[0].split('.')
appname=parsed[0]
modelname=parsed[1]
#get the models class for the specified contenttype
model_class=ContentType(app_label=appname, model=modelname).model_class()
#create class Mixin, containing the dropcode property
class DragToTagable:
dropcode = dropcode
#add DragToTagable as a base class to the model class
model_class.__bases__+=(DragToTagable,)
except AttributeError:
pass
except:
import sys
print "Unexpected error:", sys.exc_info()[0]
raise
This way i do not have to create an additional table, like in burhans proposal. And the app stays completely independent and requires no work on existing models to be implemented.
Thanks for the tips.