Development

From CSclasswiki
Jump to: navigation, search

Project planning overview

BlogSite (practice project)

We will need:

  • Articles
    • Headline
    • Body
    • Comments
    • Date published
    • Author
    • Tags
  • Users
    • Hierarchy
      • Admin (site creator, all permissions)
      • Manager (create contributors and users, create and edit all articles and comments)
      • Contributor (create users, create articles, leave comments, edit own articles, edit comments on own articles)
      • User (leave comments)
      • Viewer (view page only)
    • Attributes
      • Username
      • Password
      • First and last name
      • Date added (sign up date)
      • Articles created
  • Pageviews
    • blog view: show all articles in reverse chronological order, just a headline, the first 20 words, first few tags. Option to view by tags instead
    • article view: see all attributes of article
    • login page
    • manager page

Global Proverbs Project

Proverbs

  • Instance
    • Language
    • Translation or original/common usage
      • If original, start date (can be unknown)
      • If original, end date (can be present day)
    • Geographic usage
      • Hierarchical system by continent, country, region, city. All or none of above
  • Explanation/definition - in many languages, but attached to overarching proverb rather than each translation
  • Citation: about - first usage, origins
  • Citation: usage - text field, can include books, quotes, links
  • Tags (themes etc.)
  • Relations- an attribute of each article that has relationship type (opposite, same meaning, other) and ID of other article. **When appended, adds the parallel relationship to the other article
  • Discussion/comments

Users

  • Attributes
    • ID/ username
    • Name
    • Email
  • Preferences
    • Languages
      • Display preferences (ex: only show proverbs with French translations or definitions)
    • Privacy
      • For each attribute (and contributions), show checkboxes:
        • Display to me only
        • Display to logged in users
        • Display publicly
  • Contributions (related articles)

Other issues

  • Backups
  • Concurrency

Django and Eclipse Troubleshooting

To set up BitNami Djangostack in Eclipse, once PyDev is installed: Window -> Preferences On the left click to open PyDev, then click Interpreter- Python On the right, click New to create a new interpreter, and name it BitNami Python or something to differentiate it from other versions of Python you may have installed Click Browse and select the location of BitNami’s Python (In Windows, find the location of the BitNami DjangoStack program file, then open the folder python, then select python.exe) Check all the boxes but the last one


To create a new Django project: File->New->Project PyDev-> PyDev Django Project Match the grammar version to that of the version of Python you are using as your interpreter Select “BitNami Python” (the interpreter you created above) from the drop down Interpreter menu

Note: So far we are using SQLite for simplicity

To create a project using MySql:

Create project, settings as follows:

 DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
       'NAME': 'globalProverbs',                      # Or path to database file if using sqlite3.
       'USER': 'root',                      # Not used with sqlite3.
       'PASSWORD': 'bitnami',                  # Not used with sqlite3.
       'HOST': ,                      # Set to empty string for localhost. Not used with sqlite3.
       'PORT': ,                      # Set to empty string for default. Not used with sqlite3.
   }
 }

These can be changed in settings.py.

In cmd, enter mysql. If your root password does not work it was probably not created. Create a root password, not in quotes (unless you want the quotes to be part of the password). Run the command CREATE DATABASE globalProverbs; (or whatever your databse will be named).


To run a newly created app in your project: Directly below the Navigation menu (or at least in Windows that’s where it is) is a green Run button. Open its drop down menu and click Run Configurations. In the left sidebar, right click PyDev Django and create a new configuration. Name it “Run [project name]” Project: Browse -> your project Main module: Browse - > your project -> manage.py Click the arguments tab. Under program arguments add “runserver --noreload” for Windows, one hyphen for Macs. Click Apply then Run to run. Henceforth always run this configuration- it will be an option directly in the Run drop down menu.

The reason we do this is that with reload enabled, it will not be possible to stop BitNami service- as soon as you end it with "Terminate All" in Eclipse or with "Stop BitNami Djangostack Service" in BitNami itself, it was automatically reload, and the only way to stop the service is to manually kill each instance of python.exe running under Processes in the Task Manager. The downside of noreload is that it is often necessary to stop and restart the app in order for new changes to be visible.


When you are done editing etc., click Terminate or Terminate All, the red buttons above the bottom window, to stop the BitNami service.

While BitNami is running, go to 127.0.0.1:8000 to see your Django project.

In Eclipse, to run anything that in Django alone would require use of the command prompt and begin with “python manage.py”, right click the project name in the left sidebar. Under Django there are the most common options (syncdb, test) as well as the option of a custom command. When entering custom commands you do not need to begin with python manage.py.

To open the shell, right click the project name -> Django -> shell with Django environment. It will open in the Console tab at the bottom.


If in Part 2 you get the error message “Error: No module named pollsdjango.contrib.admin,” check settings.py: did you forget to add a comma after ‘polls’ ? You’ll need to syncdb again after you change this, because you changed settings.py.


Any DatabaseError- trying running syncdb and refreshing before you try anything else. If tables or columns are not being created, and you are willing to lose all the data saved in your database, you can run

 python manage.py reset [appname]
 python manage.py syncdb

NOTE: This will delete all data other than users and groups and whatever is hard-coded in, so this should only be done during site creation when all data is test data and can be lost safely.


For error DatebaseError: database is locked: Terminate all, syncdb, close Eclipse and reopen.


As recommended in the Django documentation, instead of deleting users, mark them as inactive. By default this will still allow them to log in, however.


Something worth noting: The comments app that comes with Django attaches comments based on ID- that is to say, if you have a comment on Article 1, then delete Article 1 but assign a new article id=1, the comments from the first Article 1 will still be visible, and will be attached to this new Article 1. This is also true after a complete database wipe, because, like users, comments are a separate app and thus are not stored in the database.


If, when submitting a formset, a `ValidationError: No exception supplied` is raised, with the traceback reading "Management data is missing or has been tampered with," it may be that TOTAL_FORMS and INITIAL_FORMS are not being automatically supplied as they should be. Adding { {formset.management_form} } (remove spaces between brackets) in the template with every formset should fix this problem. Source: [1]


For error TemplateDoesNotExist, compare TEMPLATE_DIRS in settings.py and views.py. If settings has

 TEMPLATE_DIRS = (
   "[...]/mytemplates/blogsite",
 )

and views has

 return render_to_response('blogsite/article/detail.html')

then Django will search for the directory [...]/mytemplates/blogsite/blogsite/article/detail.html - note the extra blogsite folder, which creates the error.

Additional note: make sure that in TEMPLATE_DIRS there is a comma after the string if it is the only one, so that it will be recognized as a tuple.


Using a text editor in Eclipse: The simplest solution is Eclipse's Web Page Editor. To install: Go to "Help" > "Install New Software" Choose to work with the site "http://download.eclipse.org/releases/helios" (replace helios with release name if not helios), Expand "Web, XML and Java EE development", Check "Web Page Editor" and click Next to continue with the install

Source: [2]

To use, right click on an html file and choose Open with -> Web page editor

This is optional. Open with -> HTML editor is also fairly good, though less useful.

Useful external links

How to pass in variables to a base template: Create a custom template context processor. [3]

Regular expressions [4]

ManyToManyField documentation (also has sample blog project) [5]

Limiting access to content to users that pass some test using decorators (documentation) [6]

Displaying a form using a template (documentation) [7]

Django's default comments framework (documentation) [8]

Creating django's default forms from pre-exisitng models (documentation) [9]

Extending the user model (to add language preferences) [10]

Adding to autodiscover admin fields [11]

Code Documentation

To change the way an object is referred to, add to its class definition

def __unicode__(self):
     return self.name

where [name] is an attribute of the class object that you would like referenced as the name. Without this, every object will be referred to as "[class] object".


Inlines with ManyToManyFields: Example with article= models.ManyToManyField(Article) in class Tag:

 class TaggingInline(admin.TabularInline):
   model=Tag.article.through

Then register inlines=[TaggingInline] in ArticleAdmin.


When in class Tag (models.py)

 article= models.ManyToManyField(Article)

was changed to

 article= models.ManyToManyField(Article, related_name='article')

then

 list_filter = ['tag', ]

in class ArticleAdmin (admin.py) yielded the following error message:

 'ArticleAdmin.list_filter[0]' refers to 'tag' which does not refer to a Field.

It is not possible to have two list_filters or two search_fields, only the second will be implemented.


Make sure to save newly created objects before trying to associate them with any other objects. Related error message:

 Cannot add "<Tag: NewlyTagged>": instance is on database "default", value is on database "None"

To resolve this issue, save the new tag and try again.


Custom widgets work differently when they are acting on attributes of a model and when they are acting on attributes of another model connected through ManyToManyField.

Example: tag is an attribute of the class Tag. To use the custom widget AddTagWidget on the Tag Admin page, this is the code that goes in admin.py:

 class TagAdminForm(ModelForm):
   class Meta:
       model = Tag
       widgets = {
           'tag': AddTagWidget
       } 
 class TagAdmin(admin.ModelAdmin):
   form = TagAdminForm
 admin.site.register(Tag, TagAdmin)

Tag has a ManyToMany relationship with Article. This is how to use the same custom widget on the Article Admin page:

 class ArticleAdminForm(ModelForm):
   tag = forms.CharField(widget=AddTagWidget())
   class Meta:
       model = Article
 class ArticleAdmin(admin.ModelAdmin):
   form = ArticleAdminForm
 admin.site.register(Article, ArticleAdmin)

When changing an attribute of an object during form validation, you must save the form before changing the attribute. Example:

   if pform.changed_data:
              pform.save()
              proverbinstance= pform.cleaned_data['id']
              proverbinstance.last_edit = datetime.datetime.now()
              proverbinstance.save()

This works.

   if pform.changed_data:
              proverbinstance= pform.cleaned_data['id']
              proverbinstance.last_edit = datetime.datetime.now()
              proverbinstance.save()
              pform.save()

This does not work, even though the Proverb object to which the attribute last_edit is attached is saved. It is also possible for the form.save() to not register because of the earlier save. For that reason, it is best to save attributes by accessing them directly, rather than through the form.

   if pform.changed_data:
              pform.save()
              proverbinstance= pform.cleaned_data['id']
              if proverbinstance:
                     pi = ProInst.objects.get(id=proverbinstance.id)
                     pi.last_edit = datetime.datetime.now()
                     pi.save()

This is also true of deleting things, for a different reason. The form cannot be deleted and then saved, because the deleted instance will be recreated when the form is saved. You must either save all edits first and then delete as necessary, or, preferably, delete all items marked for deletion and then apply edits and save (if [marked for deletion]: delete, else: edit and save).


To get data from a custom html input inserted directly into the template, like

   <input type=checkbox name="delete" value="delete">

use

   request.POST.getlist('delete')

which in this case will return [u'delete'] for every box checked. It is more helpful to use { {forloop.counter} } as a value, etc.


Outline for article editing:

  • html files:
    • Article view /article/1 (has edit button)
    • button directs to new page /article/1/editing (has submit button)
    • button posts to article/1/edit- unseen
    • redirects to article/1/edited
  • note- no file edit.html, because /edit is never seen, just used to submit data
  • views.py:
    • articledetail: displays page (/article/1)
    • articleediting:displays page w/ form (/article/1/editing)
    • articleedit: deals with POST data (/article/1/edit)
    • articleedited: displays page (/article/1/edited)

Outline for article adding:

  • html files:
    • Articles view /articles(has add button, visible only if privledged)
    • button directs to new page /articles/add (has submit button)
    • button posts to article/added- unseen
    • redirects to article/__id__/detail
  • views.py:
    • articles: displays all (/articles)
    • articleadd: displays page w/ form (/articles/add)
    • articleadded: deals with POST data (/article/added)
    • articledetail: displays article (/article/2)

On passing variables into templates: https://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext

A shortcut: The locals() trick, http://www.djangobook.com/en/beta/chapter04/

Database Information

On monitoring database interaction: https://docs.djangoproject.com/en/1.2/faq/models/#faq-see-raw-sql-queries

Optimization: https://docs.djangoproject.com/en/1.2/topics/db/optimization/

Project creation log

Blogsite project and app (a simplified version of the goal product) created- July 6

  • All levels of users and respective permissions- July 6
  • Articles: Headlines, body text, date published- July 6
  • Many-to-many tags attached to articles- July 8
  • Adding tags by text- July 11
  • Simple page views- July 13
  • User login and authentication- July 14
  • Article editing in page view- July 14
  • Add or remove tags by text in article edit page view- July 15
  • Add articles- July 15
  • Minor changes, finalization- July 15