Category Archives: Programming

Chosen selects in the django admin

A frustrating hour wasted and I don’t have enough rep to contribute to this stackoverflow answer, so I’m posting here. To add the jquery chosen widget to the django admin panel (django 1.10, chosen 1.6.2), so that multi select widgets show up properly, you need to do the following:

models.py – Get your manytomany set up

class Threat(models.Model):
    name = models.CharField(max_length=300, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']


class Species(models.Model): 
    threats = models.ManyToManyField(Threat, blank=True)

admin.py – Register it in the admin site and add custom js and css

class SpeciesAdmin(admin.ModelAdmin):
    class Media:
        # Put your jquery in - automatically included by django but it appears below the chosen.jquery.min.js, adding it again just seems to shift it above
        js = ('//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js', 
              'chosen/chosen.jquery.min.js',
              'chosen_admin.js')
        css = {'all': ('chosen/chosen.min.css','chosen_admin.css')}

admin.site.register(models.Species, SpeciesAdmin)

chosen_admin.js – Easy, just instantiate chosen as you want

$(document).ready(function() {
    options = {
      search_contains: true, // plus whatever else you want 
    }
    $('#id_threats').chosen(options);
});

chosen_admin.css – This was the annoying bit to pinpoint and fix

.field-threats, .field-threats .related-widget-wrapper {
	overflow: visible !important;	
}
.chosen-container-multi .chosen-results {
	width: 100%;
}

Thanks to radtek and bradmontgomery.

TDWG 2016

I had the privilege of being sent to the Biodiversity Information Standards (TDWG) group annual conference a few weeks ago, in December 2016, held in Costa Rica. The event was hosted by what must be the most friendly and kind group of people in the country (either that or all Costa Ricans are incredibly kind), at the Tecnologico de Costa Rica.

logoFinalReducido

Costa Rica was as amazing as when I last visited, although La Fortuna (nearby which the conference was held) was extremely rainy. I’m talking pretty much constant rain; the whole week we were there I think I saw the sun perhaps twice? The conference included a really cool field trip into the rainforest, where my colleague and I got absolutely soaked (no surprise there!) but we did see an awesome tarantula in its hole and a beautiful waterfall.

The conference itself was one of the best experiences I’ve had of conservation biology since I completed my CB course at the Fitzpatrick Institute. Everyone I met was intelligent, knowledgeable, turned on, engaged and curious about the field.  It felt like a very meaningful conference: biodiversity data might not be the sexiest subject ever (and “Biodiversity Information Standards” sounds downright boring), but it’s absolutely crucial for solving our modern environmental problems and that’s something which was at the forefront of everyone’s minds.  Continue reading

Adjust or change hidden value in form before submit with JQuery does not work

So here’s something rather odd, if you have a form:

<form id="process" action="/form-submit-url" method="post">
  <input id="myinput" name="mydata" type="hidden" />
  <button type="submit">Submit</button>
</form>

And you try and populate the hidden mydata input using jquery before the form submits:

 $(document).ready(function() {
   $('#process').submit(function(event) {
     $('#myinput').val('hello world')
   });
 });

It won’t work. You can’t do any of these sort of things either:

 $('input[name=mydata]').val('hello world');
 $('#myinput').attr('value', 'hello world');
 etc 

You have to do:

$('input[name=mydata]').val('hello world'); 

Quite strange. See http://stackoverflow.com/questions/2979772/set-value-of-hidden-field-in-a-form-using-jquerys-val-doesnt-work

The non-jquery method works fine!

document.getElementById("mydata").value = 'hello world';

Validation examples for handsontable

In my current project, I have users who want to regularly copy and paste stuff from excel and save it into a database via a web app. At first I tried an elaborate system where they would download a spreadsheet template, paste their data into it, upload it and get back a new spreadsheet with validation errors. It started getting quite complicated, and I was very happy to find the handsontable library (an awesome lightweight spreadsheet library) to use instead. The problem is that there aren’t many examples on how to add validation on the web. So I hope this helps some other poor soul like me.

First thing is to stop trying to use handsontable’s methods via jquery. I spent ages trying to get it working and getting terribly confused because examples on the internet were all using jquery but the documentation on the site was all just pure js. Definitely easiest to go the pure js way, don’t forget you can include pure js code in your jquery $(document).ready(function(){}) – it will work just fine. JQuery is just javascript after all!

So, here is an example on a few different types of validation methods:

 HTML:
<div id="exampleGrid" class="dataTable"></div>
<button id="submitData" type="button" class="btn btn-primary">Upload</button>

JS: (remember to include jquery for your bits and bobs, as well as handsontable's js and css)
var myData = [
 ["cat", '10/11/2015', ''],
 ["invalidvalue", '10/11/2015', ''],
 ["dog", '10/11/2015', ''],
 ["", '10/11/2015', '']
];

// Empty validator
emptyValidator = function(value, callback) {
 if (!value || 0 === value.length) {
 console.log('false');
 callback(false);
 } else {
 console.log('true');
 callback(true);
 }
};

// Handsontable options
hotOptions = {
 colHeaders: ['autocomplete','date', 'string'],
 minSpareRows: 0, // This must be 0 or else it tries to add a blank row as soon as you remove it for validation
 rowHeaders: true,
 contextMenu: true,
 colWidths: [240, 100, 100],
 columns: [
 { // Note, whenever you put "type: something", handsontable does its own validation and if you add a "validator: yourfunction" it seems to go haywire.
 type: 'autocomplete',
 source: ['cat', 'dog'],
 strict: true
 },
 { // It is more obvious with the date that some kind of validation is going on and it will get confused with your own custom validator. I don't know how you would add custom date validation, I haven't had to do that yet thank goodness
 type: 'date',
 dateFormat: 'DD/MM/YYYY',
 correctFormat: true,
 defaultDate: '01/01/2000'
 },
 { // Here is the empty column with your validator forcing text in there
 validator: emptyValidator
 }
 ],
 data: myData,
};

// Instantiate
var ht = new Handsontable(document.getElementById('exampleGrid'), hotOptions);

// Trigger more validation with the click
$('#submitData').click(function() {
 // Get the data in the cells
 var myTableData = ht.getData();

 // If the last row is empty, remove it before validation
 if(myTableData.length > 1 && ht.isEmptyRow(myTableData.length - 1)) {
 // Remove the last row if it's empty
 ht.alter('remove_row', parseInt(myTableData.length - 1), keepEmptyRows = false);
 }

 // Validate the cells and submit the form via ajax or whatever
 ht.validateCells(function(result, obj) {
 if(result == true) {
 console.log('submitted');
 }
 else {
 console.log('bad form data')
 }
 });
});

And here is a link to a jsFiddle: https://jsfiddle.net/sysb6wvd/2/

Django-allauth and modelforms

It took me a ridiculously long and frustrating time to work out how to do this, so I’m documenting what I’ve done in the hope that it helps someone else. If you want to extend the Django user model (to create a user profile, as described in the docs https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#extending-the-existing-user-model) to create a user profile, and to still use Django-allauth you might as well give up on the documentation: it’s not going to help you. But what you need to do is fairly simple:

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    phone = models.CharField(max_length=100)

forms.py

class SignupForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('first_name', 'last_name', 'phone', 'type')

    # A custom method required to work with django-allauth, see https://stackoverflow.com/questions/12303478/how-to-customize-user-profile-when-using-django-allauth
    def signup(self, request, user):
        # Save your user
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

        # Save your profile
        profile = Profile()
        profile.user = user
        profile.phone = self.cleaned_data['phone']
        profile.type = self.cleaned_data['type']
        profile.save()

settings.py

# Required by django-allauth to extend the sign up form to include profile data
ACCOUNT_SIGNUP_FORM_CLASS = 'core.forms.SignupForm'

I’m not sure why I thought that django-allauth would take care of saving user profiles when it very explicitly says it doesn’t do anything apart from authentication. I suppose we should count ourselves lucky that it’s easy to extend the form to include additional profile fields.

Django multi select multiple choice model field

To me, it doesn’t really make sense to store static data in a database – especially if there are only 5 or 6 errr datum you are wanting to store. Django allows you to use a Charfield with choices, i.e.

WIND = 'W'
SOLAR = 'S'
ENERGY_TYPE_CHOICES = (
    (WIND, 'Wind turbine'),
    (SOLAR, 'Solar panels')
)
energy_type = models.CharField(max_length=1, choices=ENERGY_TYPE_CHOICES, default=WIND)

But unfortunately you can’t select multiple choices, so in this case it wouldn’t be possible to associate both wind & solar with one of my models. It is suggested that developers store this info in a separate table and associate the choices with models using a manytomanyfield. Perhaps this makes sense if you want to associate the field options with multiple models, but it really seems overkill if you just want to have multiple choices for a single model.

Strangely there don’t seem to be many other people who have felt this to be too much (I mean, going by google anyway). As I have frequently found with Django, a few people have discussed it on stackoverflow and there are a million ways of doing the same thing with no clear indiciation as to which is most widely used and most sensible. Well, there are 3 different modules/alternatives:

  • Multiselectfield: https://github.com/goinnn/django-multiselectfield
  • Multiple_select_field: https://gist.github.com/kottenator/9a50e4207cff15c03f8e
  • Select_multiple_field: https://github.com/kelvinwong-ca/django-select-multiple-field

This is ridiculous. Django’s “do-it-yourself” attitude is refreshing after coming from PHP and Drupal, but why isn’t something this ubiquitous integrated into Django itself?  This is something many projects are going to want. Or do I have it wrong and is it better to have a separate db table for like 2 or 3 fields?

Django error: TemplateResponseMixin requires either a definition of ‘template_name’ or an implementation of ‘get_template_names()’

This is such a bizarre error message and I have spent hours trying to figure out what on earth I’ve been doing wrong. This is my situation: I have several models for which I am creating generic views for (ListView, DetailView, CreateView, UpdateView, DeleteView). These mostly contain postgis geographical data that I am storing as polygonfields and so on. To display these nicely in my forms I want to use the django-leaflet LeafletWidget. To do THIS I have to create a modelform. So for example my files look like:

models.py:

class Project(models.Model):
    current_name = models.CharField(max_length=50)
    location = models.PolygonField()
    objects = models.GeoManager()

views.py:

class ProjectCreate(CreateView):
    model = models.Project
    template_name_suffix = '_create_form'
    form_class = forms.ProjectCreateForm

forms.py:

class ProjectCreateForm(forms.ModelForm):
    class Meta:
        model = Project
        fields = ('current_name', 'location')
        widgets = {'location': LeafletWidget()}

urls.py:

urlpatterns = [
    url(r'^project/create$', views.ProjectCreate.as_view(), name='project_create'),
]

now, if you leave out the model = x bit in the createview (which i think is perfectly reasonable as you have to specify in the modelform in the forms.py what model you want to use, then you get this error:

TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

Absolutely infuriating. I can’t believe how many hours it took for me to track this down. All because I left it yesterday and didn’t put the model = x bit back into the code when I removed it to see what happened. I also can’t believe I actually forgot about it though – senility is clearly on the horizon.

It is actually in the docs though – https://docs.djangoproject.com/en/1.8/topics/class-based-views/generic-editing/ scroll down to the first note inset. Lesson learned: always read the docs rather than googling for stackoverflow answers!

WordPress, you suck for websites with lots of pages and no posts

My recent experiences with wordpress have made me realise that it really is a terrible solution for a website with a lot of pages (say more than 30) and no posts. It’s great at blogging though. But say you have lots of pages, you’re gonna want a menu to show those pages right? And you’re probably going to want 2 menus, one for the ‘about/contact/partners/faq’ to-do-with-the-company content, and one for the primary content. If your primary content custom menu is likely to be long (as it is in my case, at around 130 pages+), then the default menu system is cumbersome and dragging things around between levels is a complete nightmare.

And there is no solution. You can display page hierarchies instead, but this sucks because it’s useful to think about your site in terms of the menu structure, and to do half of your menus using custom menus and half of your menus using page hierarchy is shit. It’s also impossible to get your menus to automatically sync with your page hierarchy. I miss drupal. Here’s hoping this post annoys someone enough for them to post and tell me an easy way of doing what I want (just a simple menu which is easy to manage!).

Django 1.7 and Python 3.4 how to add a label class to your ModelForm using crispy forms

There’s a bug that’s described here: http://stackoverflow.com/questions/22901745/django-crispy-forms-bootstrap-helper-label-class-and-helper-field-class-not-wo

which can be “fixed” by changing crispy_forms/templates/bootstrap/field.html on line 8 from:

<label for="{{ field.id_for_label }}" class="control-label {% if field.field.required %}requiredField{% endif %}">

to

<label for="{{ field.id_for_label }}" class="control-label {% if field.field.required %}requiredField{% endif %}{% if label_class %} {{ label_class }}{% endif %}">

I have no idea how to get field classes working, frankly. But it is possible to get a fieldclass using django by passing it in through a widget thank goodness. Like so:


class BookForm(forms.ModelForm):
class Meta:
model = Book
exclude = ('unecessary_field')
widgets = {
'published': forms.TextInput(attrs = {'class': 'col-lg-8-or-whatever'}),
}

Took me ages to work this out. I hope it helps someone.

Fixed navigation (scrolling to content headings)

I used http://www.distractedbysquirrels.com/blog/one-page-layout-with-fixed-navigation-and-jquery/ as a foundation for a fixed navigation/scrolling type page, and modified it so it was a bit nicer and more adaptable, like this:

$(document).ready(function() {
	// Parameters
	var navOffset 	= 15,
		scrollSpeed = 500;
		animationSpeed = 100;
		cssClass = 'scrollNavOn';
		minTop = $('#content').offset().top,
		maxTop = $('#content').height() + minTop - $('#navigation').height();	
		
	// Place navigation
	var currentScroll = $(window).scrollTop();
	$('#navigation').css({'top' : minTop});
	
	// Align navigation after loading while scrolling through the content
	if( currentScroll > minTop && currentScroll < maxTop ) 
		$('#navigation').css({'top' : navOffset + 'px'});	
	
	// Adjust navigation top to content top and end of content
	if( currentScroll <= minTop ) 
		$('#navigation').css({'top' : minTop - currentScroll});
	if( currentScroll >= maxTop ) 
		$('#navigation').css({'top' : maxTop - currentScroll});
	
	// Get section positions to use later
	var sections = [];
	$('.scroller-sections').each(function() {
		sections.push([$(this).attr('id'), Math.floor($(this).offset().top)]);
		var currentSection = $(this);		
	});
	
	// Set the click functionality for the nav
	$("#scroller-navigation li:not(.notsection)").each(function(index) {
		$(this).click(function (event) {
			event.preventDefault();
			var targetOffset = sections[index][1] - navOffset;
			$('html,body').animate({scrollTop:targetOffset}, scrollSpeed);
		});
	});

	// On scroll highlight the correct nav item
	newNavIndex = 0;
	$(window).scroll(function() {
		// Current scroll
		var winScroll = $(window).scrollTop();
		
		// Align navigation to window scroll while scrolling through content
		if( winScroll > minTop && winScroll < maxTop )
			$('#scroller-navigation').css({'top' : navOffset + 'px'});
			
		// Adjust navigation top to content top and end
		if( winScroll <= minTop ) 
			$('#scroller-navigation').css({'top' : minTop - winScroll});
		if( winScroll >= maxTop ) 
			$('#scroller-navigation').css({'top' : maxTop - winScroll});
		
		// Iterate through the different sections and work out which nav element to highlight
		for(var i = 0; i < sections.length; i++) {
			// Work out which section we are on - get the top of our current section and the top of the next section
			var lowerLimit = sections[i][1] - navOffset
			var upperLimit = $(document).height(); // If we are on the last section  set upperlimit to be document height
			if(i < sections.length - 1) 
				upperLimit = sections[i + 1][1];

			// Run through each section height and see whether it's between the upper and lower limits, and also whether we've scrolled down to the end of the document
			if(	((winScroll + navOffset) < upperLimit && (winScroll + navOffset) > lowerLimit) || 
				(i == sections.length - 1 && (winScroll + $(window).height() == $(document).height()))	) {
				// $('#scroller-navigation li:nth-child(' + (i + 1) + ')').css({'font-weight' : 'bold'});

				$('#scroller-navigation li:nth-child(' + (i + 1) + ')').addClass(cssClass, animationSpeed);
				$("#scroller-navigation li:not(.notsection)").each(function(index) {
					if(i != index)
						$(this).removeClass(cssClass, animationSpeed/2);
				});				
			}
		}
	});		
});

With HTML like this:

<html> 
  <head> Add your CSS and JS here </head> 
  <body> 
    <div id="wrapper"> 
      <div id="sidebar"> 
        <ul id="scroller-navigation"> 
          <li>List your nav items here</li> 
        </ul> 
      </div> 
      <div id="content"> 
        <div class="scroller-sections" id="uniqueid"> 
          <h2>Heading</h2> 
          <p>Content</p> 
        </div> 
      </div> 
    </div> 
  </body> 
</html>

What someone really needs to do now is automatically build the <li>s based on how many h2s (or whatever, that should be passed in as a parameter I guess) there are in the text and set the nav up automatically.