Assignment 5: Web Application with Models, Forms, and Images; ORM Queries
- Assignment 5: Web Application with Models, Forms, and Images; ORM Queries
- Learning Objectives
- Case Study: Mini Insta
- Preliminaries
- Continue working on the mini_insta Application
- Task 1: Revising the Photo model; uploading Photos
- Task 2: Updating an existing Profile
- Task 3: Updating and Deleting Posts
- Task 4: Additional data models: Follow, Comment and Like
- Task 5: Creating a Post Feed
- Task 6: Search and Results
- Deployment to cs-webapps
- Submitting Your Work
due by 9:00 p.m. EDT on Friday 2/27/2026
Learning Objectives
After completing this assignment, students will be able to:
-
Use the Django ImageField to store and retrieve image files and their associated database records.
-
Use HTML forms to upload a image files to Django, including setting foreign keys to create 1-to-many relationships.
-
Use HTML forms to display images stored in the Django media directory.
-
Implement update and delete operations by inheriting from generic views, and creating appropriate URL routing in response to update and delete operations.
Pre-requisites: Assignments 4
Assignment 5 builds upon the case study you began in Assignment 3.
You must have completed Assignment 4 before you attempt Assignment 5.
This assignment requires the Python Imaging Library, Pillow to be installed.
If you have not already installed it in your virtual environment do so now:
# pipenv install Pillow
Case Study: Mini Insta
This assignment is part of an in-depth case study to develop a data-enabled web application. In the entire case-study, you will simulate the core features of the Instagram social networking application. Over the course of 5 parts, you will implement:
- Viewing, creating, and updating user profiles
- Viewing and creating posts
- Uploading images
- Viewing and adding followers
- Developing a feed to show posts from Profiles one is following
- Incorporating user accounts, authentication (login), and registration
This assignment is the third and fourth parts of the case study.
Here is a sample implementation: MiniInsta part 3 (uploading images, update/delete profile and posts).
MiniInsta part 4 (followers, likes, comments, and news feed).
You do not need to match the data of this example, or even the way it is formatted. The look and feel of the application, as well as the data you dispaly, is entirely up to you.
Preliminaries
In your work on this assignment, make sure to abide by the collaboration policies of the course.
If you have questions while working on this assignment, please post them on Piazza! This is the best way to get a quick response from your classmates and the course staff.
For each problem in this problem set, we will be writing or evaluating some Python code. You are encouraged to use the VS Code IDE which will be discuss/presented in class, but you are welcome to use another IDE if you choose.
Important Guidelines: Comments and Docstrings
-
Refer to the class Coding Standards for important style guidelines. The grader will be awarding/deducting points for writing code that conforms to these standards.
-
Every program file must begin with a descriptive header comment that includes your name, username/BU email, and a brief description of the work contained in the file.
-
Every function/method must include a descriptive docstring that explains what the function does and identifies/defines each of the parameters to the function.
Continue working on the mini_insta Application
Continue to do this work within your existing Django project, within the mini_insta.
application that you worked on in Assignment 4.
Task 1: Revising the Photo model; uploading Photos
In the previous iteration of this assignment (i.e., assignment 4), we created a model
for a Photo and stored the image_url associated with an image stored elsewhere on the
World-Wide-Web. In this iteration of the assignment, we will add storage for image
objects within the Django media directory.
-
Revise the
Photomodel, to add an additional attribute calledimage_file. This attribute will refer to an image file (not a URL) that is stored in the Django media directory.**Do not remove the
image_urlattribute, as we need this for backwards-compatibility with existingPhoto.Add an accessor method called
get_image_url, which will return the URL to the image. This URL will either be the URL stored in theimage_urlattribute (if it exists), or else the URL to theimage_fileattribute, i.e.,image_file.url.Revise the
__str__method to be consistent with the way the actual image is stored, i.e.,image_urlvs.image_file.Recall that after you change models, you will need to run the
makemigrationsscript and themigratecommand to update your database.Important:
-
Before you can go on to step 2 (displaying a
PostwithPhotos stored byimage_file), you will need to add at least 1Photo(and maybe even 2) to your database using theadmintool to upload the file. -
When you add an
Phototo the database, you will need to also associate it with aPostobject. Take note of whichPost(for whichProfile) this is, so that you can test that thePhoto(s) display properly (see below).
-
-
Displaying a post. You might need some revisions to the code that displays
Posts andPhotos, e.g. theshow_post.htmlpage. Revise to ensure that you are using theget_image_urlmethod to access the image’s URL.Verify that it works for both:
-
one the
Posts for which you stored an image URL. -
one the
Posts for which you uploaded an actual image.
-
Revise the process for creating a Post. Specifically, you will need to revise
the CreatePostForm, the create_post.html template, and the CreatePostView.
-
In the
CreatePostForm, revise to remove theimage_url. While it is still part of the model for backwards-compatibility, it should no longer display for the user to enter a URL. -
In the
create_post.htmltemplate, you will need to to create a form field to be able to select and upload one or more actual image files. For example:
Use the form
input type="file", as well as the attribute"multiple"to be able to select more than one. Be sure to give this field a name, so that you can look for it in the form processing code (below).Be sure to include the attribute
enctype="multipart/form-data"in the<form>tag, so that the form will be able to send the image data over HTTP. -
In the
CreatePostViewclass, you will need to modify the code in theform_validmethod. Previously, you retrieved theimage_urlfrom therequest.POSTdata and used that to create aPhotoobject. Comment this part out.In it’s place, you will need to read the data from
self.request.FILES. Try readingfiles = self.request.FILES.getlist('files'), which will return a list of 0 to many files. You can process this with a loop to create and savePhotoobjects. -
Test everything! Start at this URL pattern:
'http://127.0.0.1:8000/mini_insta/. Go to any profile, and then use the create post form to upload an image. Test that the image gets stored in Django, and finally that it displays on theshow_postpage.
Important: Add files to git!
-
You’ve just reached a good stopping point.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Task 2: Updating an existing Profile
-
Create a new form class
UpdateProfileFormwhich inherits fromforms.ModelForm.Be sure to specify the inner-class
Meta, which relates this form to theProfilemodel. Specify the list offieldsthat this form should set (i.e., all of the data attributes of theProfileclass, except NOT the user’susernameandjoin_date, which should not be changeable). -
Create a class-based view called
UpdateProfileView, which inherits from the genericUpdateViewclass. Be sure to specify the form this create view should use, i.e., theUpdateProfileForm. Also, specify the name of the template to use to render this form, which must be calledmini_insta/update_profile_form.html. -
Create the template file
mini_insta/update_profile_form.html, to render the HTML form.You may use your discretion/imagination about how this template should display the form fields, but you must include labels for each field to show what input is expected. Upon submission, your form will be handled by the generic
UpdateViewclass, which will update theProfilerecord for this model and store the update in the database.On this page, include a ‘cancel’ button, which should stop the update operation and return to the profile page. For example:

-
Edit the
mini_instaproject’surls.pyfile. Create a URL mapping to route requests from the URL pattern'profile/<int:pk>/update'to theUpdateProfileViewview. Name this URLupdate_profile.Test your URL! Try this URL pattern:
'http://127.0.0.1:8000/mini_insta/profile/1/update(or a similar URL with a different primary key).You should see a form to update the profile, and it should be pre-filled with the existing data for this
Profilerecord. Change something in the form, and use the submit button to submit it to the server.A complication: Implementing the
get_absolute_urlmethodTest out this URL again. Now when you submit the
create_post_form.htmltemplate, you should see an error:No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model.To solve this problem, you will need to implement the special method
get_absolute_urlon theProfilemodel class. Within that method, you will need to return the URL corresponding to theProfilethat was updated. For example, if we just updated aProfilewith primary key of 5, we should return the URL/mini_insta/profile/5.Upon submission you should be routed back to the profile page, on which you should see the update.
If you have errors, resolve them now before continuing on to the next part.
Finally, add an
Update Profilelink or button to theshow_profile.htmltemplate, so that you can click that link and reach the update form. For example:
Important: Add files to git!
-
You’ve just reached a good stopping point.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Task 3: Updating and Deleting Posts
In this part, you will create a way for to the user to update or delete a Post.
This will involve:
-
Links (or buttons) on the
Postpage labeledupdateanddelete. -
A view to handle the each of these operations.
-
A template to show a confirm/cancel option (form).
-
A
urlto trigger each operation.
For example, notice the links for update and delete:

These buttons (actually links that look like buttons) link will connect to the URLs for
the DeletePostView and UpdatePostView, respectively.
-
Create a class
DeletePostView, which inherits from the genericDeleteViewclass. This base class will do most of what we want, but we will need to override some methods below. Set themodelandtemplate_nameattributes.Override the
get_context_datamethod, which will provide the context data needed to support thedelete_post_form.html. Specifically, this context must provide the variables:-
post, which is a reference to thePostto be deleted. -
profile, which is theProfileof the user who made thePost.
Override the
get_success_url(self)method, to return the URL to which the user should be redirected after a successful delete operation. Specifically, when aPostis deleted, the user should be redirected to the profile page of the user whosePostwas deleted. -
-
Create the template file
delete_post_form.html. This template will display thePostthat will be deleted, and contain 2 small forms:-
One to confirm the delete, which will send a
POSTback to the same URL at which you are trying to do the delete. -
One to cancel the delete, which will send a
GETto URL to redisplay the profile page.Hint: you will need to set the
actionattribute of the form to the URL to show the profile page. You will need to create a URL that returns to the profile page, but you have created this URL elsewhere in your code. Think about where you did this previously!
For example:

-
-
Create a new URL pattern to trigger the delete
Postprocess. Use this URL pattern:post/<int:pk>/delete, and associate this URL with yourDeletePostViewclass.A concrete example of such a URL would be:
http://localhost:8000/mini_insta/post/11/deletewhich means to delete the
Postwith primary key of 11. -
Within your
show_post.htmltemplate, you will need to add a link (titleddelete). Within this link, you will need to create the correct URL pattern to trigger the URL defined in the previous step.{% url 'delete_post' post.pk %}where
postis the name ofPostobject within the context of the template that displays thePost. -
Test that the new delete link works – it should show up in the
show_postpage. Now try to click the link. Check that it generates the correct URL (for example:http://localhost:8000/mini_insta/post/11/delete).If it does not generate a correct URL for the delete operation, go back and fix that now.
After deleting a
Post, the user should be redirected to theProfilepage for the person whosePostwas deleted. -
Repeat steps 1-5 for the update operation. Create a class
UpdatePostViewwhich inherits from the genericUpdateViewclass, create a template calledupdate_post_form.htmlwith confirmation/cancel options, and a URL pattern:post/<int:pk>/update, and associate this URL with yourUpdatePostViewclass.The requirement for the update
Postfeature is to be able to update the caption text of thePost. After updating aPost, the user should be redirected to theshow_postpage for thatPost. -
Test everything!!
Important: Add files to git!
-
You’ve just reached a good stopping point.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Task 4: Additional data models: Follow, Comment and Like
Read before Create
-
In this section, we will add 3 new data models for
Follows,Comments, andLikes. -
In this assignment, we will use the Django Admin tool to create data for these models, but we will not implement features to create these through our web application. We will return to creating these data objects in Assignment 7.
-
Define a new data model called
Follow, which encapsulates the idea of an edge connecting two nodes within the social network (e.g., when oneProfilefollows anotherProfile).A
Followrelation will associate 2Profiles. Use these data attribute names:-
profile(a foreign key) indicating whichprofileis being followed (i.e., the “publisher”). -
follower_profile(a foreign key) indicating whichprofileis doing the following (i.e., the “subscriber”) -
timestamp(a date time field) indicating the time at which the follower began following the otherprofile.
Note: Two
ForeignKeys to the same model-
Django will complain about a
reverse_accessor, because you have 2ForeignKeys with the same model.To fix this, add the parameters
related_name="profile"andrelated_name="follower_profile"to eachForeignKey, respectively.
Write a
__str__method so that you can view thisFollowrelationship as a string representation. It would be helpful to include the display names or usernames of bothProfiles in this string representation. For example for aFollowrelationship between Angela Merkel and Taylor Swift, we could create this string representation:Angela Merkel follows Taylor Swift.Use the admin tool to add several
Followrelationships. You don’t need a lot, but perhaps NFollows for NProfiles to get started. Leave at least 2Profiles without followers (for testing purposes), and to be able test adding followers later (in next week’s assignment). -
-
Write the following accessor methods on the
Profileclass:-
A
get_followersmethod, which will return a list of thoseProfiles who are followers this profile (i.e., subscribers who will seePosts from this profile). Providing this method will make it possible to call the method (among other places) in the template, i.e., to display a profile’s followers.This method will need to use the Django ORM (i.e.,
Follower.objects) and its methods to filter/retrieve matchingFollowerrecords.- This method must return a
listof the followers’Profiles (not aQuerySetorlistofFollows). Pay attention to the data types.
- This method must return a
-
A
get_num_followersmethod, which will return the count of followers. -
A
get_followingmethod on theProfileclass, which will return a list of thoseProfiles followed by this profile (i.e., publishers to whosePosts thisProfileis subscribed).Providing this method will make it possible to call the method (among other places) in the template, i.e., to display the profiles a user is following (subscribed to).
This method will need to use the Django ORM (i.e.,
Follower.objects) and its methods to filter/retrieve matchingFollowerrecords.- This method must return a
listof theProfiles being followed (not aQuerySetorlistofFollows). Pay attention to the data types.
- This method must return a
-
A
get_num_followingmethod, which will return the count of how many profiles are being followed.
-
-
Write the following views, templates, and URLs:
-
Create the
ShowFollowersDetailViewandShowFollowingDetailViewview classes, each of which will inherit from the genericDetailView. These classes areDetailViews for aProfile, and will provide the context variableprofileto their respective templates. -
Create the templates
show_followers.htmlandshow_following.html, which will use the context objectprofileto call the accessor methods on theProfilemodel, to retrieve the required lists ofProfiles to display. -
Create the URL patterns
'profile/<int:pk>/followers'and'profile/<int:pk>/following'to bring up theShowFollowersDetailViewandShowFollowingDetailView, respectively.
Modify the
show_profile.htmltemplate to include displaying the count of Followers and Following, with links that bring up the views for each.For example:

-
-
Define a new data model called
Comment, which encapsulates the idea of oneProfileproviding a response or commentary on aPost.A
Commentwill associate the following data attributes:-
post(a foreign key) indicating thePostto which thisCommentis related. -
profile(a foreign key) indicating whichprofileis doing the commenting. -
timestamp(a date time field) indicating the time at which thisCommentwas created. -
text(a text field) which is the substance of theComment
Write a
__str__method so that you can view thisCommentas a string representation.Use the admin tool to add several
Comments on at least 2 differentPosts. You don’t need a lot, just enough for testing purposes.Create an accessor method on the
Postclass calledget_all_commentsto retrive all comments on aPost. This method will need to use the Django ORM (i.e.,Comment.objects) and its methods to filter/retrieve matchingComments for aPost.Update the
show_post.htmlpage to add display ofComments below thePostcaption. -
-
Define a new data model called
Like, which encapsulates the idea of oneProfileproviding approval of aPost.A
Likewill associate the following data attributes:-
post(a foreign key) indicating thePostto which thisLikeis related. -
profile(a foreign key) indicating whichprofileis doing the liking. -
timestamp(a date time field) indicating the time at which thisLikewas created.
Write a
__str__method so that you can view thisLikeas a string representation.Use the admin tool to add several
Likess on at least 2 differentPosts. You don’t need a lot, just enough for testing purposes.Create an accessor method on the
Postclass calledget_likesto retrive all likes on aPost. This method will need to use the Django ORM (i.e.,Like.objects) and its methods to filter/retrieve matchingLikes for aPost.Update the
show_post.htmlpage to add display ofLikecount above thePostcaption. For example:
-
-
Test everything! Start at this URL pattern:
'http://127.0.0.1:8000/mini_insta/.
Important: Add files to git!
-
You’ve just modified your database structure, and added some data records.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Task 5: Creating a Post Feed
-
The Instagram “post feed” is a scrollable view that includes
Posts,Photos,Comments, andLikes (and lots of ads!), customized to each user (Profile).Briefly, the post feed shows posts for each of the profiles being followed by a given user, with the most recent at the top.
Write an accessor method
get_post_feedon theProfileobject, which will return alist(orQuerySet) ofPosts, specifically for the profiles being followed by the profiles on which the method was called.Hint: it will be easiest to develop this by using the ORM to filter
Posts.You might experiment with using the object managers (e.g.,
Follow.objects,Post.objects) at the Django python console to discover which methods to use to generate thelist/QuerySetthat you want.You may also use python tools (e.g., list comprehension,
forloop, method calls) to build/refine results.Hint: begin with a simple if not perfect version, and then you can refine it later.
Test this method at the console to prove that it returns some data, of the correct type, before you try to build the view/template for it.
-
Create a new template called
show_feed.htmlto display the post feed for a singleProfile. The post feed should show eachPost, along with the profile image and name of the person who wrote it.Create a new view class
PostFeedListViewwhich inherits fromListView, and associate it with theshow_feed.htmltemplate. Theshow_feed.htmltemplate should display all of thePosts in the feed, and eachPostshould include the firstPhotoof thePost, the caption,Likes, andComments.Create a new URL pattern:
'profile/<int:pk>/feed'and associate it with thePostFeedListView. -
Navigation
-
Change the “home” navigation link (e.g., the leftmost navigation icon in the example) from the bottom of the page so that it links to the post feed.
-
If you do not already have one, add a navigation link to the profile page (e.g., the rightmost navigation icon in the example).
-
Add a link from the post feed back to the profile page.
For example:

-
-
Test everything! Start at this URL pattern:
'http://127.0.0.1:8000/mini_insta/.
Important: Add files to git!
-
You’ve just reached a good stopping point, and added some data records.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Task 6: Search and Results
-
Add a search feature to search user
Profiles andPosts based on a text input. Implement a view class calledSearchViewwhich inherits from the genericListView. Use 2 templates calledsearch.htmlandsearch_results.html, and a URL pattern'profile/<int:pk>/search'(i.e., the search is being done on behalf of the user with the profile specified bypk).In configuring the
ListView, set thetemplate_nameto be'search_results.html'. This will be the default way to display results of this search.Here are some implementation notes:
-
The
search.htmlpage should present an HTML form with a text area search box as well as a button to submit the search. Use an HTML form (not a model form, since this is not creating or updating model data). This form should collect aqueryto search against the data models.For example:

-
The
search_results.htmlpage should present two different listings of results: one forProfiles that match the query, and one forPosts that match the query. Consider how you would like this to appear, and use a separate loop for each.For example:

-
-
In the
SearchViewclass, you will need to implement (override the super-class version) several methods:-
dispatch(self, request, *args, **kwargs): this method is called first to dispatch (handle) any request. Override this method to add an if statement, testing for the namequeryinself.request.GET.If the
queryis absent, return thesearch.htmltemplate, i.e., to provide the form to collect thequeryfrom the user. Be sure to include theprofilecontext variable, since the template will need this variable to create the URLs.Otherwise, return the superclass version of
dispatch, i.e.,super().dispatch(request, *args, **kwargs), which will continue the work of the genericListView. -
get_queryset(self): the genericListViewuses this method to obtain theQuerySetof instance data (that matches themodelattribute of aListView. We did not set that attribute, because we do not want all objects to be returned, but rather we want to control which objects to return.In this method, write a query against the object manager (i.e.,
Post.objects) to obtainPosts that contain thequery. Return theQuerySetof matchingPosts. -
get_context_data(self, **kwargs): this method returns the dictionary of context data that can be accessed from the template. Add the following to the context data:-
the
profileobject (for whom we are doing this search) -
the
query(if present) -
the
poststhat match the query, obtained fromget_query_set. Apostmatches if the query is contained in itscaptiontext. -
the
profilesthat match the query, obtained from using the object manager (Profile.objects). Aprofilematches if they query text is found in itsusername,display name, orbio text.
-
-
-
Edit the
base.htmltemplate, and add a link to the bottom-of-screen navigation, to bring up thesearchpage (in its own page view). -
Test everything! Start at this URL pattern:
'http://127.0.0.1:8000/mini_insta/.
Important: Add files to git!
-
You’ve just modified your database structure, and added some data records.
-
This is an excellent time to add your files to git, commit your changes, and push your changes to GitHub before anything gets F@&#ed up.
Deployment to cs-webapps
- Deploy your web application to the
cs-webapps.bu.edu.
Follow the deployment instructions here.
-
Test your web application on
cs-webapps.bu.eduto ensure that everything works correctly. -
Resolve any deployment issues.
Submitting Your Work
10 points; will be assigned by the autograder, verifying that you have submitted the correct files/URL, and testing that you website exists at the specified URL. 90 points; will be testing your application and code review
Log in to GradeScope to submit your work.
Be sure to name your files correctly!
-
Create a text file within your main django directory called
mini_insta_url.txt, containing the URL to your web page, and nothing else.For example:
https://cs-webapps.bu.edu/azs/cs412/mini_insta/.This file will be used by the autograder to locate your web page, so you must get the URL exactly correct, and you must not include any other text or code in the file.
-
Add the teaching staff as collaborators to your GitHub repository with
bu-cs412. Read-only access is fine.Create a text file called
github_url.txtin the root of your project (e.g.,djangodirectory). Paste your GitHub URL in the file.Add these files to your git repository using
`git add -A`.
Commit this to your git repository, using
`git commit -m "Added mini_insta_url.txt"`Push it to GitHub, using
`git push origin main`
-
Log in to GradeScope to submit your work.
In the Gradescope upload page, upload these two files:
-
mini_insta_url.txt -
github_url.txt
-
Notes:
-
Upload these files to Gradescope before the deadline.
-
When you upload, the autograder script will process your file(s).
-
You may resubmit as many times as you like before the deadline, and only the grade from the last submission will be counted.