Does someone know a simple way to set the default zoom and latitude/longitude with floppyforms.gis.BaseGMapWidget ? And additionally, when creating an entry set panning as default tool.
In the form i define:
class PointWidget(floppyforms.gis.PointWidget, floppyforms.gis.BaseGMapWidget):
map_width = 690
map_height = 300
class EventForm(ModelForm):
class Meta:
model = Event
widgets = {
'coordinates': PointWidget(),
}
This perfectly shows the map widget and i can set a point. It also nicely centers & zooms the point when loading the form for an existing entry.
But when displaying the form to create a new entry it centers at the west-cost of africa..
Is there a way to achieve this (preferably by defining in the form/widget-class and not using additional javascript).
First, override get_context_data() on your widget class and point it
to a custom template:
class PointWidget(floppyforms.gis.BaseGMapWidget, floppyforms.gis.PointWidget):
template_name = 'custom_lonlat.html'
default_lon = 'something'
default_lat = 'something'
def get_context_data(self):
ctx = super(PointWidget, self).get_context_data()
ctx.update({
'lon': self.default_lon,
'lat': self.default_lat,
})
return ctx
Then create the custom_lonlat.html template:
{% extends "floppyforms/gis/google.html" %}
{% block options %}{{ block.super }}
options['default_lon'] = {{ lon }};
options['default_lat'] = {{ lat }};
{% endblock %}
If you want to make the default lon/lat dynamic, you can set them directly on the form instance from your views code:
form = EventForm()
form.fields['coordinates'].widget.default_lon = 'something'
form.fields['coordinates'].widget.default_lat = 'something else'
I'm the author of floppyforms and I've seen this question a couple of times… I'll add this answer to the official docs soon.
Related
I'm trying to use django-jfu to multiupload images, but I have a problem. I want to handle a foreign key dynamically (via url or something), but I can't think of anything.
I have the following models:
class Event(models.Model):
name = models.CharField(max_length=128)
class Picture(models.Model):
event = models.ForeignKey(Event)
image = models.ImageField(upload_to='media')
According to django-jfu, you have to specify a "upload" view to call from the template via template tag. This is my upload view:
#require_POST
def upload(request):
event = Event.objects.get(id=26)
file = upload_receive(request)
instance = Picture(image = file, event = event)
print instance
instance.save()
basename = os.path.basename(instance.image.path)
file_dict = {
'name' : basename,
'size' : file.size,
'url': settings.MEDIA_URL + basename,
'thumbnailUrl': settings.MEDIA_URL + basename,
'deleteUrl': reverse('jfu_delete', kwargs = { 'pk': instance.pk }),
'deleteType': 'POST',
}
return UploadResponse(request, file_dict)
Right now, as a test, it only saves pictures to event with id=26, but how can I handle it dynamically? This is the view and template where I'm calling the template tag:
view
def add_pictures_to_event(request, event_id):
return render(request, 'add_pictures_to_event.html')
template
{% extends 'base.html' %}
{% load staticfiles %}
{% load jfutags %}
{% block body %}
<div class="container">
<h2>Photo upload</h2>
{% jfu %}
</div>
{% endblock %}
As you can see, the view add_pictures_to_event, gets the request and the id of the event, but I cant seem to pass it to the upload view.
Any help would be appreciated.
I had the same question. I looked at different django versions of jQuery File Upload but stuck with Alem's jfu but with the changes from Thomas Willson to make it work in 1.9. My solution might not be the best but I could not find an other way.
I assume you already created an event and then add images to it.
media_upload_form.html is in my projects static directory. I used the UPLOAD_FORM_EXTRA block to add a hidden formfield with the current event_id:
{% block UPLOAD_FORM_EXTRA %}
<input type="hidden" name="currentevent" value="{{instance.pk}}">
{% endblock %}
I assume you have the view from the docs. I changed in the beginning of the uploadview:
file = upload_receive( request )
event_instance = get_object_or_404(Event, id=request.POST['currentevent'])
instance = Picture( file = file, event=event_instance)
instance.save()
It is probably against all django rules but it works. If anyone has a better solution I like to know too. FormSets maybe?
I noticed that maps in the Geodjango admin have a menu on the right.
How can I add more layers than just one representing the model field the map is related in admin?
This is my model (model.py):
class Foresta(models.Model):
nome = models.CharField("Nome", blank = False, max_length = 255)
descrizione = tinymce_models.HTMLField("Descrizione", blank = True, help_text='Inserire una descrizione del bosco')
slug = models.SlugField("Slug", blank = True)
published = models.BooleanField("Pubblicato")
...
coord = models.PointField("Coordinata punto foresta", blank = False)
# GeoDjango-specific: a geometry field (MultiPolygonField), and
# overriding the default manager with a GeoManager instance.
mpoly = models.MultiPolygonField("Mappa foresta (poligono)", blank = False)
objects = models.GeoManager()
This is my admin model (admin.py):
class ForestaAdmin(admin.OSMGeoAdmin):
default_lon= 1308296
default_lat= 5714101
default_zoom= 9
overlays = ('coord', 'mpoly')
I tried with the 'overlays' option but without success!
You can find the default OSMGeoAdmin settings here. Unfortunately, it doesn't look like you can accomplish this with something as simple as specifying an overlays tuple. Instead, it looks like you should create a custom map template and then override the map_template option in your ForestaAdmin class with the path to your template. Start by copying the default openlayers template and customize the javascript to add your other layers as additional OpenLayers Vector layers.
I had to add extra readonly multipolygon on the map. I redefined osm.html (?just changed osm.js file path cause django used default "gis/admin/osm.js")
{% extends "gis/admin/openlayers.html" %}
{% block openlayers %}{% include "gis/osm.js" %}{% endblock %}
osm.js:
{% extends "gis/admin/osm.js" %}
{% block extra_layers %}
{% if extra_wkt %}
var extraLayer = new OpenLayers.Layer.Vector("extra_layer");
var extraGeometry = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.fromWKT('{{ extra_wkt }}'),
{}, // attrs
{
fillColor: "#8a8a8a",
fillOpacity: 0.4,
strokeColor: "#000000",
strokeOpacity: 0.6,
strokeWidth: 1,
}
)
extraLayer.addFeatures(extraGeometry);
{{ module }}.map.addLayer(extraLayer);
{% endif %}
{% endblock %}
There is I use extra_wkt value which passed as context.
(mpoly - name of geodjango multipolygon field)
from django.contrib.gis.admin import OSMGeoAdmin, OpenLayersWidget
class OSMLayersWidget(OpenLayersWidget):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
if name == "mpoly":
extra_mpoly = MultiPolygon([..., ...])
if extra_mpoly:
srid = self.params['srid']
if extra_mpoly.srid != srid:
try:
extra_mpoly.transform(srid)
extra_wkt = extra_mpoly.wkt
except GDALException as err:
logger.error(
"Error creating geometry from value '%s' (%s)",
extra_mpoly,
err,
)
extra_wkt = ''
else:
extra_wkt = extra_mpoly.wkt
context["extra_wkt"] = extra_wkt
return context
admin.py:
class ServiceZoneAdmin(OSMGeoAdmin):
map_template = 'gis/osm.html'
widget = OSMLayersWidget
Result:
You can use olwidget http://docs.olwidget.org/en/latest/django-olwidget.html. It allows you to edit and show different layers (inside and outside Django admin). However, you might find it difficult to use depending on which Django version are you running.
Finally, I managed to do it as #garnetb said by modifying the openlayers.js file found in (my case) here:
/usr/local/lib/python2.7/dist-packages/django/contrib/gis/templates/gis/admin/openlayers.js
If you can't find it just look for it in the osm.html and/or openlayers.html. So, this is the way I managed to add a second layer. My objective was to add a "visual" only layer, but you can easily modify the controls if you want to edit it. So, let's assume you have a model like this one:
class Lines(gis_models.Model):
name = gis_models.CharField(max_length=10)
geom = gis_models.MultiLineStringField(srid=4326)
geom_points = gis_models.MultiPointField(srid=4326, null=True)
objects = gis_models.GeoManager()
def __unicode__(self):
return self.name
So you see, there are 2 elements being load, the first one are lines and the second one are points. So my objective is to add the lines as a visual reference for when I'm working with the points. To be able to do that, go to openlayers.js and look for the section where the Base Layer is being defined and add something like this.
// Base Layer
{% if field_name != "geom" %}
geom_layer = new OpenLayers.Layer.Vector("visual");
{{ module }}.map.addLayer(geom_layer);
var wkt_vis = document.getElementById('id_geom').value;
if (wkt_vis){
var features = {{ module }}.read_wkt(wkt_vis);
geom_layer.addFeatures(features);
}
else {
alert("no wkt id field");
}
{% endif %}
if ({{ module }}.is_point) {
var style = new OpenLayers.Style({
pointRadius : 4,
strokeColor : 'red',
strokeWidth : 2,
strokeOpacity : 1,
fillColor : 'white',
fillOpacity : 1
});
var layer_style = new OpenLayers.StyleMap({
'default' : style,
});
{{ module }}.layers.vector = new OpenLayers.Layer.Vector(" {{ field_name }}", {styleMap : layer_style});
}
else {
{{ module }}.layers.vector = new OpenLayers.Layer.Vector(" {{ field_name }}");
}
As you can see, the whole idea is that openlayers.js is loaded for every layer, but you can still access other objects. This configuration left me with a handy admin where I'm showing two maps. In the first one, I show the lines and I'm able to modify them. In the second one, I show the lines as a reference and the points are editable. The second part shows you how to set up the points style (just in case...) This is just a simplified example. If you need more details just let me know. I also managed to add a "delete feature" control. I only tried this with GeoDjango base on Django 1.6, but it should work with other versions as far as they use openlayers.js in the same way.
I've been scanning through Django documentation, and Google search results, all afternoon and I'm still somewhat stuck in my attempt to create a dynamic form. I'm hoping I just need someone to nudge me in the right direction :-) I'm just starting to learn Django, so I'm still very much a beginner; however, I'm already an intermediate python user.
What I'm trying to do is create a dynamic form, where the user makes a selection from a drop-down menu, and based on that selection another part of the form will automatically update to display results relevant to the currently selected item, but from another database table.
I'll try and use a simplified version of the models from the Django tutorial to better illustrate what I'm trying to do:
# models.py
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
So lets say I want to have something like a drop-down selection field, populated with the question from each Poll in the database. I also want to have a text-field, which displays the corresponding choices for the currently selected Poll, which will update on-the-fly whenever the user selects a different Pool. I've been able to figure this out by placing a button, and posting information back to the form; However, I'm trying to do this automatically as the user makes a selection. My view sort of looks something like this at the moment:
#view.py
from django import forms
from django.shortcuts import render_to_response
from myapp.models import Poll,Choice
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s" % obj.question
class PollSelectionForm(forms.Form):
polls = MyModelChoiceField( queryset=Poll.objects.all() )
class ChoiceResults(forms.Form):
def __init__(self, newid, *args, **kwargs):
super(ChoiceResults, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.TextField( initial="" )
def main(request):
return render_to_response("myapp/index.html", {
"object": PollSelectionForm(),
"object2": ChoiceResults(),
})
My template is very simple, just something like
{{ object }}
{{ object2 }}
I'm sure the way I'm going about creating the forms is probably not the best either, so feel free to criticize that as well :-) As I mentioned, I've read solutions involving reposting the form, but I want this to happen on-the-fly... if I can repost transparently then that would be fine I guess. I've also seen libraries that will let you dynamically create forms, but that just seems like overkill.
Here is one approach - Django/jQuery Cascading Select Boxes?
You can create a new view that just renders json to a string,
and then trigger an event when you're done selecting from the first list which loads the data dynamically from that json.
I do a similar thing here, populating a form based on a selection in a drop down. Maybe this helps you.
Here is the model of the values used to pre-populate the form:
class OpmerkingenGebrek(models.Model):
opmerking = models.CharField(max_length=255)
advies = models.CharField(max_length=255)
urgentiecodering = models.CharField(max_length=2, choices=URGENTIE_CHOICES_2011)
bepaling = models.CharField(max_length=155,blank=True,null=True)
aard = models.CharField(max_length=3, choices=AARD_CHOICES)
The view that manages the form:
def manage_component(request,project_id,.....):
# get values for pre-populate
og = OpmerkingenGebrek.objects.all()
.........
formset = ComponentForm(request.POST,request.FILES)
.........
)))
return render_to_response(template, {
'formset':formset,
........
'og':og,
},context_instance=RequestContext(request))
The html the renders the form
{% extends "base.html" %}
{% block extra_js %}
<script type="text/javascript" src="/media/js/limitText.js"></script>
<script type="text/javascript" src="/media/js/getValueOpmerking.js"></script>
{% endblock %}
<form enctype="multipart/form-data" method="post" action="">
{{ formset.as_table }}
</form>
<p>Choose default values:</p>
<select id="default" onChange="getValue(this)">
{% for i in og %}
<option value="{{ i.opmerking }} | {{ i.advies }} | {{ i.urgentiecodering }} |
{{ i.aard }} | {{ i.bepaling }}">{{ i.opmerking }}</option>
{% endfor %}
</select>
The javascript that pre-populates the form:
function getValue(sel)
{
//get values
var opm = sel.options[sel.selectedIndex].value;
//split string to parts
var parts = opm.split("|");
// autofill form
var opmerking = document.getElementById("id_opmerking");
opmerking.value = parts[0];
var aanbeveling = document.getElementById("id_aanbeveling");
aanbeveling.value = parts[1];
var opt = document.getElementById("id_urgentie");
var urgentie = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[2].split(' ').join('')){
opt.selectedIndex = i;
}};
var opt = document.getElementById("id_aard");
var aard = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[3].split(' ').join('')){
opt.selectedIndex = i;
}};
var bepaling = document.getElementById("id_bepaling");
bepaling.value = parts[4];
};
I've been scanning through Django documentation, and Google search results, all afternoon and I'm still somewhat stuck in my attempt to create a dynamic form. I'm hoping I just need someone to nudge me in the right direction :-) I'm just starting to learn Django, so I'm still very much a beginner; however, I'm already an intermediate python user.
What I'm trying to do is create a dynamic form, where the user makes a selection from a drop-down menu, and based on that selection another part of the form will automatically update to display results relevant to the currently selected item, but from another database table.
I'll try and use a simplified version of the models from the Django tutorial to better illustrate what I'm trying to do:
# models.py
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
So lets say I want to have something like a drop-down selection field, populated with the question from each Poll in the database. I also want to have a text-field, which displays the corresponding choices for the currently selected Poll, which will update on-the-fly whenever the user selects a different Pool. I've been able to figure this out by placing a button, and posting information back to the form; However, I'm trying to do this automatically as the user makes a selection. My view sort of looks something like this at the moment:
#view.py
from django import forms
from django.shortcuts import render_to_response
from myapp.models import Poll,Choice
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s" % obj.question
class PollSelectionForm(forms.Form):
polls = MyModelChoiceField( queryset=Poll.objects.all() )
class ChoiceResults(forms.Form):
def __init__(self, newid, *args, **kwargs):
super(ChoiceResults, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.TextField( initial="" )
def main(request):
return render_to_response("myapp/index.html", {
"object": PollSelectionForm(),
"object2": ChoiceResults(),
})
My template is very simple, just something like
{{ object }}
{{ object2 }}
I'm sure the way I'm going about creating the forms is probably not the best either, so feel free to criticize that as well :-) As I mentioned, I've read solutions involving reposting the form, but I want this to happen on-the-fly... if I can repost transparently then that would be fine I guess. I've also seen libraries that will let you dynamically create forms, but that just seems like overkill.
Here is one approach - Django/jQuery Cascading Select Boxes?
You can create a new view that just renders json to a string,
and then trigger an event when you're done selecting from the first list which loads the data dynamically from that json.
I do a similar thing here, populating a form based on a selection in a drop down. Maybe this helps you.
Here is the model of the values used to pre-populate the form:
class OpmerkingenGebrek(models.Model):
opmerking = models.CharField(max_length=255)
advies = models.CharField(max_length=255)
urgentiecodering = models.CharField(max_length=2, choices=URGENTIE_CHOICES_2011)
bepaling = models.CharField(max_length=155,blank=True,null=True)
aard = models.CharField(max_length=3, choices=AARD_CHOICES)
The view that manages the form:
def manage_component(request,project_id,.....):
# get values for pre-populate
og = OpmerkingenGebrek.objects.all()
.........
formset = ComponentForm(request.POST,request.FILES)
.........
)))
return render_to_response(template, {
'formset':formset,
........
'og':og,
},context_instance=RequestContext(request))
The html the renders the form
{% extends "base.html" %}
{% block extra_js %}
<script type="text/javascript" src="/media/js/limitText.js"></script>
<script type="text/javascript" src="/media/js/getValueOpmerking.js"></script>
{% endblock %}
<form enctype="multipart/form-data" method="post" action="">
{{ formset.as_table }}
</form>
<p>Choose default values:</p>
<select id="default" onChange="getValue(this)">
{% for i in og %}
<option value="{{ i.opmerking }} | {{ i.advies }} | {{ i.urgentiecodering }} |
{{ i.aard }} | {{ i.bepaling }}">{{ i.opmerking }}</option>
{% endfor %}
</select>
The javascript that pre-populates the form:
function getValue(sel)
{
//get values
var opm = sel.options[sel.selectedIndex].value;
//split string to parts
var parts = opm.split("|");
// autofill form
var opmerking = document.getElementById("id_opmerking");
opmerking.value = parts[0];
var aanbeveling = document.getElementById("id_aanbeveling");
aanbeveling.value = parts[1];
var opt = document.getElementById("id_urgentie");
var urgentie = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[2].split(' ').join('')){
opt.selectedIndex = i;
}};
var opt = document.getElementById("id_aard");
var aard = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[3].split(' ').join('')){
opt.selectedIndex = i;
}};
var bepaling = document.getElementById("id_bepaling");
bepaling.value = parts[4];
};
i've built my own dynamic form, kind of following james bennetts example.
using the following lines in forms.py
def make_question_form(request):
fields = { 'venue' : forms.CharField(widget=forms.HiddenInput()),
'survey' : forms.CharField(widget=forms.HiddenInput())}
return type('Question_Form',(forms.BaseForm,), { 'base_fields': fields })
and the following in the view to build it (I know its not truely dynamic, i plan to add the dynamics next.
question_form = make_question_form(request)
question_form.base_fields['venue'] = this_venue.name
question_form.base_fields['survey'] = this_survey.name
return render_to_response("survey/questions.html", locals(), context_instance=RequestContext(request))
but im not sure what to dowith it in the template and this is the bit that isn't really covered in the tutorials.
i've worked out that the following works
{% for base_field in question_form.base_fields %}
{{ base_field.type }}
{% endfor %}
but i thought the point of building it as a form was to be able to do something like
question_form.as_p
and wrap it in my own form tags.
have i missed the point or should as_p work (it doesn't).
You haven't instantiated the form in your view. make_question_form returns a new form class - normally when you use a form class in your view you do form = MyFormClass() or form = MyFormClass(request.POST).
So you need to do form = question_form() before the render_to_response, then you'll be able to do {{ form.as_p }} in the template.