Skip to content

website to allow musicians to find others to play chamber music with

Notifications You must be signed in to change notification settings

robinzigmond/chamber_mates

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Chamber Mates

Note for anyone testing or assessing this project:

As will become clear from the description below, 99% of the functionality of the Chamber Mates site is only available to registered users. And when registering a test account, the value you will get out of the site depends almost entirely on how many other users match your declared preferences - and it is critical for this that you are close enough to other users. While testing the site I made a large number of test users, who are spread throughout England and Wales (but not Scotland), but mostly concentrated in the West Midlands of England and other Western areas. In order to see most features of the site more naturally, I would advise to set your location to be somewhere in this broad region.

Given the relative sparsity of the site's current (fake) "users", I would also recommend making your search preferences as wide as possible to start with - look for players within 50 miles (not 5!), and reach out to a wide range of instruments and standards. Of course when the site has thousands of real users then there will be no need to state anything other than your actual preferences :)

Introduction and core purpose

This is a website built with Django. It aims to help musicians find others in their local area, with the aim of facilitating users coming together into small groups to play chamber music together. Alongside this core functionality (which comprises several parts, as will be detailed below), the site aims to have a very "social" feel, with users able to send private messages to each other, as well as messages to private forums for each "group" that is formed on the site.

This project will be submitted for Stream 3 (Full Stack Development) of the Code Institute's Diploma in Software Development.

The site is now hosted on Heroku. Please note that this is only a "staging" version of the site - most if not all of the 30-odd current users are test accounts, mostly created by me but a few by other Code Institute students who have kindly tested the site for me. And there are several features missing that I would want before launching a full "production" version of the site (see below).

The core "user story" of this website begins when a user registers with their basic login details (username, email and password), then fills out a profile giving their location, a brief few words to introduce themselves (optional), and the maximum distance they are willing to travel to meet other players. (These options are set at 5, 10, 20, 30 or 50 miles - these can be altered, or more added, via the Django admin if at a later date I decide they are not the best choices.) They will also specify which instruments they play, and for each instrument say what their rough standard is, what other instrument(s) they want to join with in a group, and what standards they would like the other players to be at. (Multiple selections are allowed for this, as is made clear to the user on the appropriate form.)

After the profile details are saved, the user will straight away be told of any potential matches, and it is made easy to browse through them all and the profiles of all those users - to see where they are, what they play, and ideally an indication of the other users' personality and musical preferences, if that user gave this information in the "personal info" section of their profile. Whether they did or not, the facility is provided on each user's public profile page to send a private message to them - and it is expected that users will send messages to each other after discovering that they match each other's preferences, to introduce themselves and find out if they would like to play music together. Facilitating such meetings is the core purpose of the Chamber Mates website.

In addition to this though, users can set up private groups - so after establishing another user is a close fit, perhaps after meeting in person but perhaps just after sending a few private messages, it is possible to invite them to start a new group. This sets up a private page on the website where the members of that group can have private discussions about anything they like - it is expected that a core purpose of this is to arrange times and places to meet up and play, but it is also encouraged to use this facility for private chat of any kind (the "Mates" part of the site's name was intended quite seriously!). Group members can also invite other users to join them if so desired. Each group also has a public page listing the members and which instruments (if any) they are still looking for - so it is possible for all users to find out about that group, and perhaps even send a message to one of the current members and make a case to be included! But each group's private message board can only be seen by its members - and the only way to join an existing group is for an existing member to send an invitation (and of course for that invitation to be accepted).

One other feature is a donations page where users can pay to show their appreciation for the site. An E-Commerce feature (using Stripe or Paypal) was a requirement of the Code Institute's guidelines - and at first I had planned to implement this via a subscription programme where users could have a free account with certain core features, or pay a subscription to unlock more features. But I abandoned this at an early stage of planning - because I wanted most of the features to be available in the "free tier", and it became increasingly hard to think of features to include in the "subscription" tier which were not needed for the core functionality, yet would in themselves create enough of an incentive for users to want to subscribe. So I decide to instead implement the online payments via voluntary donations towards the running of a site which is otherwise completely free - as can of course be seen on many real-world websites.

The site also notifies users of any new messages they receive, any new matches to their search preferences, and any groups they have been invited to, via a "notification" bar that appears at the bottom of the screen.

Some notes on User Experience

Although I would be the first to admit that I have neither the design nor the graphical skills to create a stunning-looking website, and freely admit that the "look and feel" of the site is no more than serviceable, I have made great efforts in this project to make the User Experience as good as possible. In fact I see no contradiction here - to me UX is about making sure the user is always aware of everything they are able to do, and that it is easy for the user to do all those things. Anything that the user should want to do should ideally be made available in just one click from wherever they are - and never more than 2 or 3 clicks away. (And if more than 1 click, the structure of the pages or menus navigated through should be intuitive enough that the user knows exactly what to click at each stage.) This to me is the core of good UX, and this is what I have striven for at every stage when adding features to the site.

I will list below some few examples of how I have tried to make this happen, as well as other things I have implemented to improve the user experience:

  • the core user-related functionalities (editing profile, viewing matches, messages and groups) are all available at all times from the "dashboard" dropdown on the main navigation menu.

  • on each of these core pages, there is a button to go straight back to the dashboard, as well as buttons on every page to go to what I considered the most logical locations which might be needed. For example, from an individual group page it is possible to go back to a list of all your groups.

  • everything that could reasonably be expected to be a link has been made into one. So wherever another user's username is shown - whether that is on the matches page, a group page, or in your message inbox/outbox, it is possible to click on it to go to their profile page.

  • when browing all "conversations" on a group page (the group's message board), there is not only a link to the top of that thread, but one to the most recent post as well. This will really help when there are many messages in a thread and the user is only interested in the most recent ones.

  • when you are invited to a group, you are naturally led to that group's (public) page, where alongside browsing the group's public details, you can simply select "accept" or "decline" and submit the result. If you accept, you stay on the group's page (after a barely noticeable delay for form-processing) and can now see its private features!

  • when looking at a user's profile, there are buttons on that very page to send a message to them (the form being prepopulated with their username), and also to invite them to join a group. The latter is one of the features of the site which I am most proud of, and one of the last implemented - there is actually a dropdown menu which shows any of your existing groups which have a vacancy for an instrument played by that user, as well as an option to invite them to start a brand new group. The best part is that the code which runs when the "send invite" button is clicked checks to see how many instruments that user plays which are wanted by the group - if there is just one, the appropriate invitation is sent automatically, while if there is more than one then you are directed to a form to select one of them.

  • when called upon to select a user in one of the forms - like those for sending a message or inviting someone to a group - rather than a dropdown list of all possible users, which would be completely unworkable if there were ever tens of thousands (or more!) of users, I have an autocomplete field which searches the database as you type and displays only those users whose username matches what you typed. (This functionality is provided by the django-ajax-selects package, which I talk about below.) I have seen this feature on several "real life" websites, and it allows you to easily find a user whose username you partially remember, without having to search for a needle in the proverbial haystack.

  • as an extension of the above, when inviting a user to a group to play a specific instrument, only users who have that instrument listed on their profile will be displayed. (To avoid having to hard-code a new lookup class for each instrument, I had to create the needed classes dynamically in a loop, which can be seen in groups/lookups.py. This loop in turn caused some problems in deployment, which I discuss below.)

  • a different use of the ajax functionality mentioned above, which I am very proud of and which I think works well, is when inviting a user to start a group. You first select their username, and then the radio select field for their instrument becomes instantly populated with just the instruments which that user plays.

  • on a more mundate note, I have added code to give focus to the most appropriate form field wherever this makes sense. For the message form, all 3 of the possible fields get focus at different times! (The user field for a blank form, the subject field if sending to a specific user via their profile page, and the message field if replying to an existing message.)

  • when adding a form for a new instrument on the profile form, I have added Javascript code for the screen to scroll down so that the user only has to add the instrument details, not scroll the page manually. I did the same for the form which adds a new post to a thread on a group's message board (which is hidden by default but can be revealed by a button-press).

A note on security

It will be noted that several features of the site - notably those related to the "groups" described above - are intended to be restricted to certain users. Since like all Django applications this site can only pass data to the server (outside of forms) in the form of URL parameters, it was necessary in many places to check whether the user making the request to a particular URL is entitled to see it. Hence the groups app in particular has many such checks, all of which raise Django's PermissionDenied exception (by showing the user a 403 Forbidden page) if the user is not entitled to see the page requested. This is also done in the user_messages app, where I have implemented the deletion of messages by directing to a URL whose view function does the deletion then returns the user to the page they were on previously. (I am aware that this is not the recommended way to handle deletion of database items, since it comes via a GET request, but it was by far the simplest way and by restricting permission to the user whose inbox/outbox the message is in I think that no harm can come of this.) A malicious user, or even an ignorant one who decides for some reason to enter urls with "random" parameters, must not be allowed to delete another user's messages just by inputting a particular URL into their browser - and I believe I have successfully prevented all such exploits.

Third-party code used

In addition to the pieces of code mentioned below, I should point out - and here is as good a place as any - that the 2 images on the homepage are almost certainly not available to freely use. The originals can be seen here and here (the latter is the small photo for the date April 24th 2016). For some reason, images of chamber musicians, with their instruments, obviously smiling and enjoying themselves seem to be few and far between on the web - especially as I wanted photos of amateur musicians, people who looked quite ordinary but happen to play an instrument. It is a key part of the ethos of the site I wanted to build that it caters for ordinary people who not only want to make music together, but want to have fun while doing it, and I wanted this emphaised by the choice of images on the home page.

I hope that my use of likely-copyrighted images is judged acceptable in a student project that I am making no attempt to publicise as an actual website for people to use. Should I ever expand the site and want to launch it to the world (and I would love to one day!), then I am aware that the images will have to be replaced - ideally by photos taken explicitly for this purpose.

Back End

The different Python packages used on the server side are listed in the files in the requirements folder (which split these up into those required just for deployment and those needed in development too). The key one is of course Django itself, but among the others, the following deserve special mention:

  • django-ajax-selects provides both the form widget and the backend code to enable the autocomplete dropdowns for searching the user database, which I mentioned above. I first installed this to use with the messages app, but ended up using it several more times, in more sophisticated ways, in the groups app - I dynamically created lookup classes for each instrument to search for just users who play that instrument, and also created a lookup for instruments played by a particular user, and made my own ajax calls to the relevant urls in order to display radio buttons for instrument selection after selecting a user to invite to a group.

  • django-map-widgets allowed me to make the nice Google map interface for users to select their location (GeoDjango does provided a default but it just didn't work at all for this application - showing only blurry photos of the Earth's surface, and starting with a far zoomed-in view of the ocean so that at first I thought it simply wasn't working).

  • the Python client for Google Maps allowed me to display a meaningful text description of each user's location.

  • django-forms-bootstrap gives each of the many forms on the site a look which is consistent with the Bootstrap styles used throughout. (I did need to write my own template in order to fix a problem I found in this package regarding its rendering of form helptext - see the comments in my own bootstrap/_field_help_text template).

  • the Stripe package was necessary for the donations app, allowing the form data submitted by the user to communnicate with the Stripe API and process the "payment".

On a technical level, I should also point out that the project, both locally and on Heroku, uses PostgreSQL together with the PostGIS geospatial extension, as the easiest way to implement the geospatial lookups. This is fully compatible with Django, via the django.contrib.gis package. (Which amongst other things has its own extension of Django's Model class - which contains amongst other things the definition of the Point field used to store a user's location, and allows lookups based on distance.)

Front End

Bootstrap version 3.3.7 has been used for the frontend to enable straightforward and consistent styling of the site without too much effort (since I wanted to spend most of the time available implementing the functionality of the site, in Python, Javascript and HTML template code) - including enabling a layout that works on all common screen sizes. Bootstrap's Javascript was also used - without the need to actually write any Javascript code - for the collapsible mobile navigation menu and the modals which confirm deletion of messages.

jQuery is a required dependency of Bootstrap's JS, and I have also used jQuery extensively in all my own Javascript code. This ranges from very simple features like focusing the appropriate form fields, to sophisticated things like changing the URL which is used when deleting a message so that it passes the correct primary keys to the backend Python code.

I also needed to use 2 other third-party pieces of Javascript code which depend on jQuery: the jquery-migrate plugin was necessary to make the Javascipt in the ajax-selects package work without errors, and jquery-formsest.js was the only piece of Javascript code I could find to enable the addition/removal of individual forms from a Django formset, which I needed for the "instrument" part of the profile form. I made a few changes to that code to improve the user experience, as listed in my comments at the top of the file. I must credit this page - particularly the HTML/JS section - for showing me how to do this and pointing me in the direction of the JS file under discussion.

Unfortunately there appears to be a bug in the formset.js code, which I was unable to fix and which I will explain below.

A few notes on my own code

Apps

Like all Django projects, this one is divided into different apps. I ended up with 5 apps, although they are rather unequal in size:

  • the Accounts app deals with everything related to individual user accounts. Not just the authentication details (which are handled by Django's default forms and models in django.contrib.auth, for the most part), but all the details of user profiles, the instruments played by each user, and the search criteria for each user/instrument combination. The key model for this app is what I called the UserInstrument model - each object in this model (or row in the corresponding database table) represents a single instance of an instrument played by a particular user.

The accounts app is also the one that calculates the matches for each user/instrument combination. For quite a large part of the development work on this project, the matches were only calculated when a user went to the page to see their list of matches - the required geospatial queries would be run in the view function and the corresponding objects passed to the template. But I decided to change it so that matches were actually stored in a separate database table, and for a few different reasons. The most immediate one was that I wanted to display notifications at the bottom of the screen when new matches were found - and for that it was necessary to know if a match is "new" or not. I could not see any way to do this other than to have the match instances stored in the database, with a field to mark them as new. (I actually ended up with 2 such fields, due to the need to display new matches in a different way, on the same page which makes the match no longer new when the user views it.) The other benefit which I believe comes from this is that the matches page should load faster because it is now making simple queries to the Matches table rather than a more complicated series of geospatial (and other) queries. The downside is that the processing of the profile form now takes longer when that is changed, because it is this event that now triggers updating of the Matches database. But users will naturally edit their profiles far less often than they will view the matches page, so I think this was a good choice.

  • the other major app is the groups app, which handles all the functionality connected to the private groups users can form, with several different forms, templates and models associated with that. Most of this functionality was implemented in a fairly straightforward way, although I made several choices with my eye on maintaining a good User Experience as mentioned earlier, and overall a lot of code was needed. There ended up being quite a tight coupling between the Groups app and the Accounts app, due to the previously-mentioned need to be able to invite users to a group from their profile page. So the dashboard page, which is naturally part of the Accounts app, ends up having to query the Groups table and the Invitations table in order to determine which groups that user is able to be invited to.

  • the user_messages app is a "medium-sized" app which deals with the private messaging functionality between users. (Really I think of it as the "messages" app - but I was already making use of the "messages" package in django.contrib and did not want to risk causing name collisions.) The models and forms involved here are relatively straightforward - the hardest part was making the deleting of messages work properly. This app - specifically the inbox and outbox pages - required the largest amount of custom Javascript of any of the apps, in order to handle various issues to do with message deletion, as well as to display the correct messages and update those when the "previous" and "next" buttons are clicked.

  • Finally there are two very "small" apps. The home app consists of just two simple static pages, including the homepage. And the donations app contains only the code for the donations form and associated page (as well as the banner across the top to try to attract the attention of logged-in users - without of course annoying them too much).

Javascript code

The project ended up needing a large amount of Javascript code - most of which is relatively small snippets in <script> tags within html templates, to do simple things like add focus to form fields. But some pages needed more complex code. One example, which I still decided to keep as an inline script, is on the form to start a new group, where as mentioned above I needed to make my own ajax calls to populate the "instrument they will play" form field with the instrument options for the user selected in the previous field. This involved jQuery code to construct the appropriate form elements (with the correct "value" attributes so that Django will understand the input) in response to the ajax calls.

There are 3 pages on which I felt the Javascript had got long or complex enough to be worth extracting into a separate .js file. These are:

  • stripe.js, used on the donations page to create a Stripe token for the payment once the form is submitted.

  • helptext.js, which is used on each form with helptext (currently 2 forms, the registration form and the donations form) in order to display question mark icons by the appropriate fields and have the corresponding helptext appear as a tooltip when they are hovered over (as well as when they are clicked, to keep the page mobile-friendly). Note that jQuery/Javascript code was the easiest way to add the question-mark icons because I am rendering the forms via django's very helpful auto-generation of html for the form (in particular I was using the django-forms-bootstrap version) - had it been a manual html file of course I could have added the icons directly with no need for javascript!

  • group_threads.js is a relatively simple file which is used to correctly display the table of forum threads on the page for an individual group - displaying the correct messages, and having them update when the "previous" and "next" buttons are clicked. It is a straight copy of the same functionality in the messages.js file!

  • finally, messages.js itself is, as mentioned above, by far the most complicated of the pieces of Javascript code that I wrote for this project. In addition to the same functions as just mentioned for the group threads, it includes all the front-end code needed to make the deletion of messages work properly. It uses regular expressions to replace the URL which the delete modal redirects to when its delete button is clicked, so that the URL points to the correct URL for the particular delete operation requested (this updating of the URL happens precisely when one of the trashcan icons is clicked - so it will be seen that the modal is not merely a courtesy in case of accidental clicking of a delete button, it is actually necessary here to ensure the right messages get deleted!). It should be noted that the URL-replacement only takes into account messages on the current "page" the user is viewing - this is important to stop users selecting some items, changing their mind about deleting them, then going to a different page and deleting some messages and finding they have inadvertently deleted the checked message on the previous page (which they will naturally assume would not be deleted if they cannot see the checkbox ticked when pressing the delete button). And the checkAbleToDelete() function, on similar lines, disables the "delete selected" button whenever there are no checkboxes ticked on the current page.

Bugs and problems

I am aware of one bug in the site. This is on the part of the profile form related to adding and removing instruments. I discovered through testing - and have confirmed repeatedly - that if a user adds a new form to input new instrument details, then changes their mind halfway through and deletes that form, then when the form is submitted then the instrument that was added-then-removed is nonetheless still added to the database! Although not a catastrophic bug (the user should notice the error in their profile fairly quickly, and it is easy to then delete the instrument), this is certainly not expected behaviour and is bound to be discovered if the site were to become widelly used. I discovered the bug at an early stage but, due to my limited understanding of how the jquery-formset.js script works, I was unable to figure out how to fix it. Nor does there seem to be any mention on the web of this bug, strangely enough. I suspect it might be due to changes to formsets in more recent versions of Django that have not yet been taken into account by this script's author(s).

One other problem I had, this one with CSS, related also to the profile form - in particular to the Google Map widget. This includes a text box for displaying the address of the selected point (which can also be used to search for a geographic location via typing). As my code currently stands, this box disappears from the page entirely when the screen is narrower than 695 pixels. I deliberately made this happen via a media-query - because otherwise, somewhere a bit below this size (I chose a larger size than necessary in order to play safe), this text box extends off the right-hand edge of the screen, which is a significant problem. I naturally tried quite a few things in my own CSS to try to push it onto the next line, but was unable to get anything to work. (The box is inside a div which is given the bootstrap CSS class .pull-right - which tells the element to float to the right, with an !important qualifier. When I tried to override this with float: left!important;, the box was correctly positioned but for some reason completely unknown to me, the Google map display itself then disappeared!) Given more time I could hope to fix this, but in the absence of a fix I decided that the text box was not critical, and doing without it on mobile was better than having it extend off the screen.

Another very small issue is that on certain screen sizes (observed in real life on an iPad Mini in Portrait orientation) the navigation bar is expanded but the links extend onto 2 lines. While the obvious solution to this would be to reduce the font size, I did not want to make it hard to read - and there is also the problem that the logout link includes the user's username, which can potentially be very long indeed. The better solution might have been to eliminate one of the links - but it was not obvious how to do this, and I decided that I could live with the 2-line navigation bar, especially as I had already chosen not to have it fixed to the top of the screen. (This takes up valuable screen space, and is less necessary when I have taken care to include buttons for the most important links, towards the bottom of each page.)

Features still to implement

There are a number of other features I would like the site to have before I ever launch it as a "real" website. In rough order of priority, these are as follows:

  1. An ability for users to reset their password. This is the #1 priority because without it a user who forgets their password has no way back in - and this is implemented (via an automated link to email the user a new password) on every modern website. For some reason I only thought of it at a relatively late stage, and although I believe Django makes this very easy to implement, it would first require the setting up of both an email server/address to send the messages from, and some email settings to be added to the Django project. Although I do not expect any of these to be hard, I was more focused on adding functionality to the groups app - but I recognise that it is absolutely necessary to any real-world website, and the very next feature I must introduce if I continue to work on the project.

  2. Email notifications. Once email has been set up to allow password reset, I would like to have automated emails sent out to users whenever something happens to merit that - that is, when a message is sent to them, or they are invited to join a group, or a new user signs up who makes a match with them. All these things which currently are handled by the "notification bar" at the bottom I would also like to cause an email to be sent to the relevant user - ideally of course, this email would only be sent if the user was not currently logged in.

  3. "Live" notifications. Still on the subject of notifications - these currently only appear when the user goes to a new page. If another user sends me a message, I will only get told about it when I next try to go to a different page. This is quite unlike how notifications work on sites like Facebook, where they are "live". To enable this would most likely require a major rewrite of a lot of the code - I would have to use a package such as django channels. But to do so would be a great learning opportunity, as well as enabling a great feature that many users would expect the site to have.

  4. A "diary" for groups. This is actually mentioned on the homepage text, but is something that I didn't quite have enough time to implement. I would like to add a new model to the groups app which is used to store "events" arranged by the group. These would be divided into "private" events - for example, rehearsals - and "public" ones, presumably concerts that the group wants to advertise publicly. A calendar showing both types of events (with some visual difference between the two) would appear when a member of the group views the group page, while only public ones would show for other users. And perhaps a calendar could even appear on the homepage showing all upcoming public events from all groups!

  5. A "like" button for groups. Once groups start performing in public and advertising their events through the site, it would make sense that more non-members would view their group page. At this point a nice touch would be to give users the ablility to "like" groups. And then the most-liked groups could be publicly listed somewhere on the site. (At the moment the only "natural" way to see details of a group which you are not a member of is either to be invited to the group, or to click on the group links on another user's profile, where it lists all groups which they are in. But it would be nice to have more access to the "public" part of all the group pages.) This could even result in many professional or semi-professional groups using the site for their own publicity, the same way many artists use social media at the moment.

Testing

Automated Testing

One of the intentions I had when launching the project was to make sure I wrote plenty of automated tests - but I failed here. I found it much easier, as well as more enjoyable, to write the code for features themselves rather than the code to test them. I could never have imagined taking a "test-driven" approach to this project - for I would not have known how to structure a test until I knew what the code that was being tested looked like. Nevertheless I did write some extremely basic (and basically useless) tests for the "home" app at a very early stage - and some more important tests for the crucial Accounts app at a midway point of the project. Mostly these tests worked - however I was unable to write a working test for anything to do with the "edit profile" page, because of its use of a Django formset. The reason for my difficulty is that a formset (or at least a ModelFormSet) has hidden input fields with values form-0-id, form-1-id etc. (one of these for each form in the formset) - these appear to relate to the database id of the objects represented by the forms. But I mostly wanted to test what happens when a user first fills out a blank profile form, in which case all that is displayed to them is a single empty form. This has no initial value for the "id" attribute, as I found from inspecting the html - yet Django kept throwing error messages that the field was required. I could not get it to work no matter what I tried for this value, including an empty string and the Python None value. Interestingly though the value does appear to be blank when inspecting the actual value POSTed when submitting the (no longer blank) form.

Since I was unable to find an answer to this dilemma, even after consulting Google and Stack Overflow, I simply gave up and commented out the failing tests. I did add some more to test the crucial functionality of recognising users who match, all of which passed.

Sadly I ran out of time at this point, but clearly I would need to write a substantial number of tests for the groups, donations and user_messages apps - as well as some more for the accounts app - before I have enough test coverage to be confident of launching the site into full production.

Manual Testing

Despite failing to write anywhere near as many automated tests as I had planned, I am fairly confident that most if not all of the functionality works as intended, because each new feature was tested manually by me in the browser as soon as it was added. Since I deployed the site to Heroku, several others have tested the site - although doubtless not as thoroughly as I did - and none have reported any issues. All of these manual tests while in development were carried out with Chrome on my laptop. Since deployment I have also tested with Firefox, Opera and Edge, and on other devices like my Android phone (running mobile Chrome), and my wife's iPhone and iPad mini (both running Safari), and have not encountered any problems. (I did notice that on Safari the forms were not autofocusing as I intended - but it seems that this is a deliberate part of how mobile Safari is supposed to work.

Deployment

Most of the necessary features for deploying a Python project to Heroku were familiar to me by the time I came to do so for this project - so things like adding the Procfile, the runtime.txt file, and separating out the settings and requirements into different files for different environments (as well as setting the DJANGO_SETTINGS_MODULE Heroku Config variable) came as no problem. There were a few issues though. One was with the deployment of the static files - which I did using whitenoise as recommended, yet for a long time did not work at all. I am still not sure what the problem was, because eventually in a session with my mentor I simply made an empty commit and then pushed to Heroku again and the static files worked (and they have done ever since) - so I suspect I simply forgot to push some changes to Heroku (even though I could have sworn I'd done that!).

The other issue, which came to light before that, was that initially the app crashed when starting. From paying attention to Heroku's error logs (the information only appeared there because of adding a LOGGING setting to the settings file for the staging environment), I traced the source of this problem to the lookups.py file in the groups app - the problem was that I had not yet run any migrations to create the database tables on Heroku, but the lookups files are run on startup, and this particular one references the Instruments table. Trying to run migrations itself ran into the same error - so the solution I used at first was to comment out the offending code, then push to Heroku, run the migrate command, then uncomment and push again. Since the same problem hit me a second time - for the exact same reason - when I later dropped the database in preparation to load the data from my local db, I ended up handling this with a config variable which is only set when I need the "offending" code suppressed.

Heroku's free Postgres addon was ideal to use as the database since I was already using Postgres locally. Enabling the geographic functionality was easy because Heroku has documentation telling you how to do it - note that this involved changing my app's "stack" from Heroku-16 to cedar-14 (I do not know if this was absolutely necessary, but it was recommended by some on Stack Overflow). Of course the DATABASES setting also had to be changed, and the dj-database-url package installed - but this was all straightforward. To transfer the data from my local db to the remote one, I used the python manage.py dumpdata command to output it to a json file (which I have left in the repo), then load it to Heroku with heroku run python manage.py loaddata ...

For obvious reasons of security I generated a new Django Secret Key, stored it in a Heroku config variable, and changed my settings to point to it. I also did the same for my Google Map API Key - but I encountered a slight problem here. I used an unrestricted key in development, but when deploying to Heroku I thought it sensible to get a new one and restrict the domain it could be used on. This would I believe have worked fine as regards the Javascript files used by django-map-widgets - but setting restrictions on "HTTP referrers" does not work (I got an error message) when using the Python client in the backend. It seems that I am supposed to use an IP restriction for the sever side - yet I discovered that Heroku dynos do not have fixed IP addresses. In the end, I made the API key used on Heroku also unrestricted - this could lead to "quota theft" (even though the key is not on Github and stored in a Heroku config variable, it can easily be found by going to the edit_profile page, viewing the source and reading it from the Javascript file!), but I think this is a low risk and not worth worrying about for now.

Finally, I have made no effort to hide the values of my Stripe settings, because these are only for test payments - as is the donations form on the deployed version. (It is necessary to use one of Stripe's test card numbers in order for the form to be processed without error.)

About

website to allow musicians to find others to play chamber music with

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published