MAD Study Hub

Home / Viva prep / MAD1 questions

MAD1 viva question bank

258 questions with model answers, grouped by topic. Answer out loud first, then open the answer. Tick the ones you can answer; the count is saved in your browser.

0 / 258 knownStart a mock viva
1What is HTTP and why do we say it is stateless? L1

HTTP is the application-layer request-response protocol the browser uses to talk to a web server. It is stateless because the server treats every request as independent and does not remember earlier ones. To remember a logged-in user we add state on top using cookies and sessions.

2Explain the client-server model with the example of your app. L1

The browser is the client: it sends an HTTP request such as GET /dashboard. The Flask server receives it, runs the matching view, talks to the database and returns an HTML response. The client only renders; all data and business rules live on the server.

3What is the difference between GET and POST? L1

GET asks for a resource, puts its parameters in the URL query string, should not change server state and is safe to bookmark or refresh. POST sends data in the request body and is used to create or change something, such as submitting a registration form. Never use GET for actions that modify data, and never put passwords in a URL.

4What does it mean for a method to be idempotent? Which methods are? L1

A method is idempotent if repeating the same request leaves the server in the same state as sending it once. GET, PUT, DELETE, HEAD and OPTIONS are idempotent; POST is not, since two POSTs can create two records. This is why a double-click on a booking button needs separate protection.

5Name the common HTTP status codes and what they mean. L1

200 OK, 201 Created, 204 No Content, 301/302 redirects, 400 Bad Request, 401 Unauthorized (not authenticated), 403 Forbidden (authenticated but not allowed), 404 Not Found, 405 Method Not Allowed, 409 Conflict and 500 Internal Server Error. The first digit gives the class: 2xx success, 3xx redirect, 4xx client error, 5xx server error.

6Walk through what happens when you type a URL and press Enter. L1

The browser resolves the domain to an IP address via DNS, opens a TCP connection (plus a TLS handshake for HTTPS) and sends an HTTP request. The server routes it, runs code, and returns a response with a status line, headers and body. The browser parses the HTML, fetches linked CSS, JS and images, and renders the page.

7Break a URL into its parts. L1

In https://example.com:8080/books/5?sort=asc#reviews, https is the scheme, example.com the host, 8080 the port, /books/5 the path, sort=asc the query string and reviews the fragment. The fragment is handled only by the browser and is never sent to the server.

8What are cookies and how do they differ from sessions? L1

A cookie is a small piece of data the server asks the browser to store and send back with every request to that site. A session is the per-user state kept between requests; in Flask the session dict is stored in a signed cookie, while server-side sessions keep only an id in the cookie. Cookies are the transport, sessions are the idea.

9What is the difference between query parameters, path parameters and form data? L1

Query parameters come after ? in the URL and are read with request.args. Path parameters are part of the route, like /book/<int:id>, and arrive as function arguments. Form data is in the POST body and is read with request.form.

10What is the Post/Redirect/Get pattern and why use it? L1

After a successful POST the server replies with a redirect (302/303) to a GET page instead of rendering directly. If the user refreshes, only the harmless GET is repeated, so the form is not resubmitted and no duplicate record is created.

@app.route("/add", methods=["POST"])
def add():
    db.session.add(Item(name=request.form["name"]))
    db.session.commit()
    return redirect(url_for("list_items"))
11What is the difference between HTTP and HTTPS? L1

HTTPS is HTTP carried over TLS. It encrypts traffic so passwords and cookies cannot be read on the network, protects against tampering, and lets the browser verify the server's identity through a certificate. Plain HTTP sends everything readable.

12What is the difference between a static and a dynamic website? L1

A static site serves fixed files that are the same for every visitor. A dynamic site generates the HTML per request using code and a database, for example showing your own bookings. Flask with Jinja2 builds dynamic pages.

13What are server-side and client-side rendering? L1

In server-side rendering the server builds the final HTML, as Jinja2 does, and the browser just displays it. In client-side rendering the server sends mostly data (JSON) and JavaScript builds the DOM in the browser. MAD1 mainly uses server-side rendering, with JavaScript used for extras.

14What are HTTP headers? Give examples. L1

Headers are key-value metadata sent with requests and responses. Examples are Content-Type (format of the body), Set-Cookie, Cookie, Location (for redirects), Accept and Authorization. They let client and server agree on formats, caching and identity.

15A form submits via GET and the password appears in the URL. How do you fix it? L2

GET puts fields in the query string, which ends up in history, logs and referrers. Change the form to POST and make the route accept POST.

<form method="post" action="{{ url_for('login') }}">
@app.route("/login", methods=["GET", "POST"])
16Your route only has methods=['POST'] and visiting it in the browser gives 405. Why, and how do you handle it? L2

Typing a URL makes a GET request, and the route does not allow GET, so Flask returns 405 Method Not Allowed. Add "GET" to methods and branch on request.method, or serve the form on a separate GET route. 405 is the correct response when the verb is not supported.

17Why is a 302 redirect after login better than rendering the dashboard directly in the POST handler? L2

Rendering in the POST leaves the browser URL as /login and a refresh re-sends the credentials. Redirecting to the dashboard sets a clean URL and a GET page, so refresh is safe and the logic of building the dashboard stays in one place.

18When should an API return 401 versus 403? L2

Return 401 when the caller is not authenticated, meaning there is no valid login or token. Return 403 when the caller is identified but lacks permission, for example a normal user opening an admin route. Mixing them up gives clients the wrong signal.

19The browser caches an old version of your CSS. How do you verify and fix it? L2

Open the dev tools Network tab, check whether the CSS request is served from cache, and do a hard reload with the cache disabled. To fix it permanently, change the file name or add a version query like style.css?v=2 so the browser treats it as a new resource.

20What is semantic HTML and why does it matter? L1

Semantic HTML uses tags that describe meaning, such as header, nav, main, section, article and footer, rather than generic divs. It helps screen readers, search engines and other developers understand the page structure, and makes code easier to maintain.

21What is the difference between block and inline elements, and between div and span? L1

Block elements like div, p and h1 start on a new line and take the full width. Inline elements like span, a and strong flow within text and only take their content's width. div is a generic block container and span a generic inline container.

22How does an HTML form send data to the server? L1

The form tag has action (URL) and method (get/post). On submit, the browser collects every named control as name=value pairs and sends them in the query string or the request body. Controls without a name attribute are not sent.

23Why do input fields need the name attribute? L1

The name is the key under which the value reaches the server, for example request.form['email']. The id is only used by CSS, JavaScript and label for. A field without a name is silently dropped.

24What is the difference between id and class? L1

An id must be unique in a page and identifies one element; a class can be reused on many elements and one element can have several. In CSS, ids are selected with # and classes with .. Classes are preferred for styling, ids for anchors and JavaScript hooks.

25Explain the CSS box model. L1

Every element is a box made of content, padding, border and margin from inside out. Width and height apply to the content box by default, so padding and border add to the total size. box-sizing: border-box makes width include padding and border.

26Describe inline, internal and external CSS and which is best. L1

Inline CSS uses the style attribute on a tag, internal CSS sits in a <style> block in the head, and external CSS is a separate .css file linked with <link rel="stylesheet">. External is best for real apps: one file styles many pages and the browser caches it.

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
27What are CSS selectors? Give element, class, id, descendant and child examples. L1

A selector chooses which elements a rule applies to. p is element, .card class, #main id, nav a descendant (any a inside nav) and ul > li child (direct children only). Pseudo-classes such as a:hover target states.

28How do CSS specificity and the cascade decide which rule wins? L1

Inline styles beat ids, ids beat classes (and attributes and pseudo-classes), and classes beat element selectors. If specificity ties, the rule that appears later wins. !important overrides these but should be avoided.

29Compare Flexbox and Grid. L1

Flexbox lays items out in one dimension, a row or a column, and is great for navbars and centring. Grid is two-dimensional, controlling rows and columns together, and suits page layouts and card galleries. They can be combined.

30What is responsive design and how do you achieve it? L1

Responsive design makes a page adapt to different screen sizes. Use the viewport meta tag, flexible units like percentages and rem, media queries such as @media (max-width: 768px), and a grid system like Bootstrap's.

31What is the viewport meta tag? L1

<meta name="viewport" content="width=device-width, initial-scale=1"> tells mobile browsers to use the device width instead of pretending to be a wide desktop. Without it, responsive CSS and Bootstrap breakpoints do not work properly on phones.

32What are the common input types and attributes for validation? L1

Types include text, email, password, number, date, checkbox, radio and file. Attributes like required, minlength, max and pattern give browser-side validation. This only improves user experience; the server must validate again.

33How do you serve an HTML page and a CSS file from Flask? L1

Put templates in a templates folder and render them with render_template. Put CSS, JS and images in a static folder and refer to them with url_for('static', filename=...). Flask serves /static/<path> automatically.

34How do you use browser developer tools while building a page? L1

Right-click and choose Inspect. The Elements panel shows the DOM and the CSS applied, and lets you edit styles live; the Console shows JS errors; the Network tab shows each request, its status, headers and timing. They are the first stop for debugging layout and request problems.

35Centre a div horizontally and vertically using CSS. L2

Use flexbox on the parent with a height.

.parent { display: flex; justify-content: center; align-items: center; height: 100vh; }

For horizontal centring alone, a block with a fixed width can use margin: 0 auto.

36Two rules style the same paragraph and the colour is not changing. How do you debug it? L2

Inspect the element in dev tools; the Styles pane shows crossed-out overridden rules. Check specificity, order of stylesheets and typos in the selector or file path. Fix it by making the intended selector more specific or reordering, rather than adding !important.

37Make a table that has alternate row colours and highlights on hover. L2

Use nth-child and :hover, which need no JavaScript.

tr:nth-child(even) { background: #f2f2f2; }
tr:hover { background: #ddd; }
38Open a link in a new tab safely. L2

Use target="_blank" together with rel="noopener noreferrer" so the new page cannot access window.opener or see the referrer.

<a href="https://example.com" target="_blank" rel="noopener noreferrer">Docs</a>
39The form has a checkbox but request.form does not contain it when unchecked. Why? L2

Browsers only send checked checkboxes. An unchecked one has no entry, so request.form['agree'] raises a 400 error. Use request.form.get('agree') or 'agree' in request.form to read it safely.

40What is Jinja2 and how does Flask use it? L1

Jinja2 is a templating engine that mixes HTML with placeholders and simple logic. render_template('page.html', items=items) loads the file from templates, fills in the variables and returns the final HTML string to the browser.

41What are the three Jinja2 delimiters? L1

{{ ... }} prints an expression, {% ... %} holds statements like for, if and block, and {# ... #} is a comment that does not appear in the output.

42Show a for loop and conditional in Jinja2. L1
{% for b in books %}
  <li>{{ loop.index }}. {{ b.title }}{% if b.available %} (in stock){% endif %}</li>
{% else %}
  <li>No books</li>
{% endfor %}

loop.index starts at 1 and the else branch runs when the list is empty.

43Explain template inheritance with extends and block. L1

A base template defines the common layout with named blocks; child templates use {% extends 'base.html' %} and fill the blocks. Anything outside blocks in a child is ignored. This avoids repeating the navbar and CSS links on every page.

{% extends "base.html" %}
{% block content %}<h1>Home</h1>{% endblock %}
44What is the difference between include and extends? L1

extends makes the current page a child of a layout and overrides its blocks. include simply inserts another template's rendered output at that spot, which is good for reusable fragments like a footer or a card.

45What is a macro in Jinja2? L1

A macro is a reusable template function with parameters. Define it once and call it wherever needed, for example to render a form field or a card.

{% macro card(title, body) %}<div class="card"><h5>{{ title }}</h5><p>{{ body }}</p></div>{% endmacro %}
{{ card("Hi", "Welcome") }}
46What are Jinja2 filters? Give examples. L1

Filters transform a value using a pipe: {{ name|upper }}, {{ items|length }}, {{ x|default('N/A') }}, {{ price|round(2) }}, {{ text|truncate(50) }}. They can be chained. Custom filters can be registered with @app.template_filter.

47What is autoescaping and what does the safe filter do? L1

Flask auto-escapes HTML in variables, turning < into &lt;, so user input cannot inject scripts (XSS). The |safe filter marks text as trusted and turns that protection off, so use it only for content you generated yourself, never user input.

48How does url_for work in templates and why use it? L1

url_for('endpoint', **values) builds a URL from the view function name. If you later change the route path, links update automatically. It also builds static file URLs: url_for('static', filename='style.css').

49How do flash messages work? L1

flash('Saved') in a view stores a message in the session; the next rendered template reads it with get_flashed_messages() and shows it once.

{% with msgs = get_flashed_messages(with_categories=true) %}
  {% for cat, m in msgs %}<div class="alert alert-{{ cat }}">{{ m }}</div>{% endfor %}
{% endwith %}
50What is Bootstrap and how do you include it? L1

Bootstrap is a CSS and JS framework with a responsive grid and ready components such as navbar, cards, buttons and modals. Include it from a CDN with a link and script tag in the base template, or serve local copies from the static folder.

51Explain the Bootstrap grid system. L1

A container holds a row, and a row holds up to 12 columns. Classes like col-12 col-md-6 col-lg-4 set how many columns an element spans at each breakpoint, so layout changes between phone, tablet and desktop.

<div class="row"><div class="col-12 col-md-6">A</div><div class="col-12 col-md-6">B</div></div>
52CDN versus local Bootstrap files: what are the trade-offs? L1

A CDN is quick to set up and often cached, but needs internet and depends on a third party. Local files work offline and are under your control but you must ship and update them. For a demo that might run offline, local files are safer.

53How do you pass data from a Flask view to a template? L1

Pass keyword arguments to render_template; they become variables. Objects work too, so {{ user.name }} accesses attributes. A context processor can add variables to every template.

54How would you show the same navbar on every page and highlight the active link? L2

Put the navbar in base.html and compare request.endpoint to mark the active link.

<a class="nav-link {% if request.endpoint == 'home' %}active{% endif %}" href="{{ url_for('home') }}">Home</a>
55Create a custom Jinja filter to format a date as DD-MM-YYYY. L2

Register a function with the template_filter decorator.

@app.template_filter("dmy")
def dmy(value):
    return value.strftime("%d-%m-%Y")

Use it as {{ booking.date|dmy }}.

56A user enters <script>alert(1)</script> as their name and it displays literally. Is that correct? L2

Yes, that is autoescaping working: the text is shown as characters instead of running. If it executed, the template must be using |safe or building HTML by string concatenation, which is an XSS bug to fix.

57Build breadcrumbs on a child page. L2

Pass a list of (label, url) pairs from the view, or define a block in the base template that children fill.

<nav><ol class="breadcrumb">
{% for label, url in crumbs %}<li class="breadcrumb-item"><a href="{{ url }}">{{ label }}</a></li>{% endfor %}
</ol></nav>
58Shade alternate table rows using only Jinja. L2

Use loop.cycle to alternate a class name.

{% for r in rows %}<tr class="{{ loop.cycle('odd', 'even') }}">...</tr>{% endfor %}
59The Bootstrap layout looks unstyled on mobile, though it is fine on desktop. What do you check? L2

Check that the viewport meta tag is present, that the Bootstrap CSS link loaded (the Network tab shows no 404), and that column classes include a small-screen class such as col-12. A missing viewport tag is the commonest cause.

60Why use a database instead of files for your app data? L1

A database gives structured storage, fast queries with indexes, constraints that keep data valid, transactions for safe concurrent updates and a standard query language. Plain files make searching, relating and protecting data much harder.

61What is SQLite and why is it a good fit for MAD1? L1

SQLite is a serverless, file-based SQL database embedded in the application process. It needs no installation or configuration, comes with Python, and is perfect for a single-machine project. It is less suited to many concurrent writers or large multi-server deployments.

62What are primary key and foreign key? L1

A primary key uniquely identifies each row and cannot be null. A foreign key is a column that refers to the primary key of another table and enforces that the referenced row exists, which is how relationships are stored.

63Explain one-to-many and many-to-many relationships with examples. L1

One-to-many: one user has many bookings, so the foreign key user_id sits on the bookings table. Many-to-many: students and courses; you need an association table holding (student_id, course_id) with both as foreign keys, optionally with extra columns like enrolled date.

64What is an ER diagram and what does it show? L1

An entity-relationship diagram shows entities (tables), their attributes, and the relationships between them with cardinality such as 1:N or M:N. It is the design blueprint you draw before creating tables.

65What is normalization? Explain the first three normal forms briefly. L1

Normalization organises tables to reduce redundancy and update anomalies. 1NF: each cell holds one atomic value. 2NF: no partial dependence of non-key columns on part of a composite key. 3NF: no non-key column depends on another non-key column.

66What are ACID properties? L1

Atomicity: a transaction fully happens or not at all. Consistency: it moves the database from one valid state to another. Isolation: concurrent transactions do not see each other's partial work. Durability: committed data survives crashes.

67Write SQL for CREATE TABLE with constraints. L1

NOT NULL, UNIQUE, PRIMARY KEY and REFERENCES are constraints enforced by the database.

CREATE TABLE booking (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER NOT NULL REFERENCES user(id),
  slot TEXT NOT NULL,
  UNIQUE (user_id, slot)
);
68Explain INNER JOIN and LEFT JOIN with an example. L1

INNER JOIN returns only rows that match in both tables; LEFT JOIN returns all rows from the left table with NULLs when there is no match. The query below lists every user including those with zero bookings.

SELECT u.name, COUNT(b.id)
FROM user u LEFT JOIN booking b ON b.user_id = u.id
GROUP BY u.id;
69What is the difference between WHERE and HAVING? L1

WHERE filters rows before grouping, HAVING filters groups after aggregation. For example GROUP BY user_id HAVING COUNT(*) > 3 keeps only users with more than three rows.

70What is the difference between DELETE, TRUNCATE and DROP? L1

DELETE removes selected rows and can be rolled back in a transaction. DROP removes the whole table and its structure. TRUNCATE empties a table quickly in other databases, but SQLite has no TRUNCATE; you use DELETE FROM table.

71What is a transaction and when do you need one? L1

A transaction groups several statements so they commit together or roll back together. You need it when steps depend on each other, for example reducing stock and inserting an order; if one fails, both must be undone.

72What is an index and what is its downside? L1

An index is a sorted structure (a B-tree in SQLite) on one or more columns that makes lookups and sorting faster. The cost is extra disk space and slower inserts and updates, since the index must be maintained.

73What does ON DELETE CASCADE do in SQLite and what must be enabled? L1

It automatically deletes child rows when the parent row is deleted. SQLite does not enforce foreign keys unless you run PRAGMA foreign_keys = ON for each connection, so cascades silently do nothing without it.

74How do you design an ER diagram from a problem statement? L1

Underline the nouns to get entities, decide attributes and a primary key for each, then find relationships between entities and their cardinality. Resolve M:N with an association table, then check for redundancy using normalization.

75Find the top three users by number of bookings. Write the SQL. L2

Group by user, count, sort descending and limit.

SELECT user_id, COUNT(*) AS n
FROM booking
GROUP BY user_id
ORDER BY n DESC
LIMIT 3;
76You need to store a list of tags per post. Why not a comma separated column? L2

A comma separated value breaks first normal form: you cannot index, search or join on individual tags, and constraints cannot be applied. Use a separate tag table and a post_tag association table.

77How do you find rows in table A that have no match in table B? L2

Use a LEFT JOIN and filter NULLs, or NOT EXISTS.

SELECT a.* FROM a LEFT JOIN b ON b.a_id = a.id WHERE b.id IS NULL;
78Add a column to an existing SQLite table without losing data. L2

Use ALTER TABLE. Note that create_all() will not alter existing tables, so in a real app use a migration tool such as Flask-Migrate.

ALTER TABLE user ADD COLUMN phone TEXT;
79What is an ORM and why use SQLAlchemy? L1

An ORM maps Python classes to tables and instances to rows, so you write Python instead of SQL strings. SQLAlchemy also builds parameterised queries, which prevents SQL injection, and makes switching databases easier.

80Explain engine, session and model in SQLAlchemy. L1

The engine manages the connection to the database. A session is a workspace that tracks objects, collects changes and sends them in a transaction on commit. A model is a class mapped to a table whose attributes are columns.

engine = create_engine("sqlite:///app.db")
Session = sessionmaker(bind=engine)
81How do you define a model in Flask-SQLAlchemy? L1

Subclass db.Model and declare columns with db.Column; relationships are declared with db.relationship.

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True, nullable=False)
    bookings = db.relationship("Booking", backref="user")
82How do you configure Flask-SQLAlchemy and create tables? L1

Set SQLALCHEMY_DATABASE_URI, create db = SQLAlchemy(app) and call db.create_all() inside an app context. create_all creates only missing tables and never alters existing ones.

app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
db = SQLAlchemy(app)
with app.app_context():
    db.create_all()
83What is the difference between flush, commit and rollback? L1

flush sends pending SQL to the database inside the open transaction without ending it. commit flushes and makes the transaction permanent. rollback discards uncommitted changes, and is needed after an error.

84Show common queries: get by primary key, filter, order, count. L1

first() returns None when nothing matches, while first_or_404() aborts with 404.

db.session.get(User, 1)
User.query.filter_by(email=e).first()
Booking.query.filter(Booking.price > 100).order_by(Booking.date.desc()).all()
Booking.query.filter_by(user_id=1).count()
85How do you define a relationship and what do backref and back_populates do? L1

relationship creates a Python-level link between models using the foreign key. backref automatically adds the reverse attribute on the other class, while back_populates declares both sides explicitly. The foreign key column is still required.

86Explain cascade options such as delete-orphan. L1

Cascade controls what happens to related objects when the parent changes. cascade='all, delete-orphan' deletes children when the parent is deleted or when a child is removed from the parent's collection. It acts in Python; database-level ON DELETE is separate.

87What is lazy loading and the N+1 problem? L1

By default related rows load lazily on first access, so looping over 100 users and reading user.bookings issues 1 query plus 100 more. Fix it with eager loading using joinedload or selectinload.

from sqlalchemy.orm import joinedload
User.query.options(joinedload(User.bookings)).all()
88How do you do a join and an aggregate with SQLAlchemy? L1

func.count, func.sum and func.avg map to the SQL aggregates.

from sqlalchemy import func
rows = (db.session.query(User.name, func.count(Booking.id))
        .join(Booking).group_by(User.id).all())
89How does SQLAlchemy protect against SQL injection? L1

It sends user values as bound parameters, separate from the SQL text, so input is never interpreted as SQL. The danger returns if you build raw strings with f-strings; for raw SQL use text() with bind parameters.

90How do you ensure uniqueness at the database level? L1

Add unique=True on the column or a UniqueConstraint for a combination. The database then rejects duplicates with an IntegrityError even under race conditions, which a check in Python alone cannot guarantee.

91What is Flask-Migrate and what do upgrade and downgrade do? L1

Flask-Migrate wraps Alembic to version-control schema changes. flask db init creates the migrations folder, flask db migrate autogenerates a script from model changes, flask db upgrade applies it and flask db downgrade reverts the latest one. It changes the schema without dropping your data.

92Add a new column to a model without losing data. L2

Edit the model, then generate and apply a migration. Check the generated script first; for SQLite a non-null new column needs a default.

flask db migrate -m "add phone"
flask db upgrade
93Handle a duplicate email on registration safely. L2

Catch the IntegrityError, roll back and tell the user.

from sqlalchemy.exc import IntegrityError
try:
    db.session.add(User(email=email)); db.session.commit()
except IntegrityError:
    db.session.rollback(); flash("Email already used")
94Soft delete: implement it so deleted records are hidden but kept. L2

Add a boolean column and filter by it. Deleting becomes item.is_deleted = True followed by commit.

is_deleted = db.Column(db.Boolean, default=False)
Item.query.filter_by(is_deleted=False).all()
95Queries raise 'PendingRollbackError' after an earlier failure. What happened? L2

A previous flush failed and the session is still in a failed state. Call db.session.rollback() in the except block so the session can be used again. Always rollback after a failed commit.

96Why does 'working outside of application context' appear when running db.create_all() in a script? L2

Flask-SQLAlchemy needs the app context to know which app and database to use. Wrap the call in with app.app_context(): or run it from the Flask shell.

97Change the type of a column in a live project. What are the risks? L2

create_all will not change existing tables, and SQLite supports limited ALTER. Use a migration with batch mode (render_as_batch=True), which recreates the table and copies data, and back up the database first.

98What is Flask and what are its main components? L1

Flask is a lightweight Python web microframework built on Werkzeug (WSGI and routing) and Jinja2 (templates). It provides routing, request and response objects, sessions and templates, and leaves choices like database and forms to extensions.

99Explain MVC and map it to your Flask project. L1

Model holds data and rules (SQLAlchemy classes), View is what the user sees (Jinja templates), Controller handles requests and ties them together (Flask route functions). Flask itself calls the controller a view function, so some people describe it as MVT; the separation of concerns is the same.

100What is a route and how do you define one with variables and methods? L1

A route maps a URL pattern to a function with a decorator. Converters like int, string and path validate and convert the URL part.

@app.route("/book/<int:book_id>", methods=["GET", "POST"])
def book(book_id):
    ...
101What is the difference between request.form, request.args and request.json? L1

request.form has POST form fields, request.args has query-string parameters and request.get_json() parses a JSON body. Use .get() to avoid a 400 when a key is missing.

102How do redirect and url_for work together? L1

url_for('endpoint') builds the URL and redirect(url) returns a 302 response with a Location header. Together they let you move to another page without hard-coding paths.

103What is the Flask session and why does it need a SECRET_KEY? L1

session is a dict-like object stored in a cookie that Flask signs with SECRET_KEY, so the user cannot alter it undetected. It is signed, not encrypted, so never store secrets in it. Without a key Flask raises an error when you use the session.

104What are Blueprints and when would you use them? L1

A Blueprint groups related routes, templates and static files into a module that is registered on the app. It keeps larger projects organised, for example separate auth, admin and api blueprints.

105What is an application factory? L1

A function create_app(config) that builds and returns the app and initialises extensions. It allows different configs for testing and production and avoids circular imports.

106Explain error handlers and abort. L1

abort(404) raises an HTTP exception immediately. @app.errorhandler(404) registers a function that returns a custom page for that error.

@app.errorhandler(404)
def not_found(e):
    return render_template("404.html"), 404
107What do before_request, after_request and the g object do? L1

before_request functions run before each view, for example to load the current user. after_request runs after the view and can modify the response. g stores data for the duration of one request only.

108What is a decorator and how would you write login_required? L1

A decorator is a function that wraps another function to add behaviour.

from functools import wraps
def login_required(f):
    @wraps(f)
    def wrapper(*a, **kw):
        if "user_id" not in session:
            return redirect(url_for("login"))
        return f(*a, **kw)
    return wrapper
109Describe a typical Flask project structure. L1

A top-level app.py or package with models.py, routes (or blueprints), a templates folder with base.html, a static folder for css, js and images, config.py, requirements.txt and the SQLite database file. Separate files keep models, logic and views apart.

110How do you handle a file upload in Flask? L1

Use a form with enctype="multipart/form-data", read request.files['file'], sanitise the name with secure_filename, check the extension and size, and save to an upload folder.

111What does debug=True do and why avoid it in production? L1

It enables the auto-reloader and the interactive debugger. The debugger lets anyone who reaches an error page execute code on the server, so it must be off in production.

112Add a new page /about that renders about.html. What are the steps? L2

Create templates/about.html extending the base, add a route, and link to it in the navbar with {{ url_for('about') }}.

@app.route("/about")
def about():
    return render_template("about.html")
113A route works for /items but /items/ gives 404. Why? L2

Flask treats a rule defined with a trailing slash (/items/) as a folder and redirects /items to it, while a rule defined without the slash does not match /items/. Choose one form consistently, or set strict_slashes=False.

114Restrict a page to admins only. L2

Make a decorator that checks the role and aborts with 403.

def admin_required(f):
    @wraps(f)
    def w(*a, **k):
        if session.get("role") != "admin":
            abort(403)
        return f(*a, **k)
    return w
115Two routes use the same function name and Flask raises an AssertionError. Why? L2

Flask uses the function name as the endpoint and endpoints must be unique. Rename one function or give an explicit endpoint= argument. This also bites decorators that forget functools.wraps, because every wrapped function then gets the name wrapper.

116Read the logged-in user's id from the session and show only their records. L2

Filter the query by the session id rather than any id given in the URL.

uid = session["user_id"]
bookings = Booking.query.filter_by(user_id=uid).all()
117What is a REST API? L1

REST is an architectural style where resources are identified by URLs and manipulated with standard HTTP methods, with stateless requests and usually JSON representations. For example GET /api/books/5 returns book 5.

118How do CRUD operations map to HTTP methods and status codes? L1

Create is POST (201 Created), Read is GET (200), Update is PUT for full replacement or PATCH for partial (200), and Delete is DELETE (204 or 200). Errors use 400, 404 or 409 as appropriate.

119What are good REST URL design rules? L1

Use plural nouns for resources, not verbs, and nest only when meaningful: /api/users/5/bookings rather than /getBookings. Use query strings for filtering and sorting, and version the API like /api/v1/.

120What is OpenAPI and what does an OpenAPI YAML contain? L1

OpenAPI is a machine-readable standard to describe a REST API: servers, paths, methods, parameters, request bodies, responses and schemas. Tools like Swagger UI render it as interactive docs and can generate client code and tests.

paths:
  /api/books/{id}:
    get:
      responses:
        "200": {description: A book}
        "404": {description: Not found}
121What is Flask-RESTful and how does a Resource differ from a plain route? L1

Flask-RESTful lets you write a class per resource with methods named get, post, put and delete; the HTTP method picks the function. You register it with api.add_resource.

class BookAPI(Resource):
    def get(self, id):
        ...
api.add_resource(BookAPI, "/api/books/<int:id>")
122What is reqparse used for? L1

reqparse.RequestParser declares expected request arguments with type, required flag and help message and validates them, returning 400 with an error message if they are wrong.

parser = reqparse.RequestParser()
parser.add_argument("title", type=str, required=True, help="title needed")
args = parser.parse_args()
123What does marshal_with do? L1

It formats the returned object according to a dict of fields, converting SQLAlchemy objects into JSON with only the chosen fields.

book_book_fields = {"id": fields.Integer, "title": fields.String}
@marshal_with(book_fields)
def get(self, id):
    return Book.query.get_or_404(id)
124How do you return a custom status code from a Flask-RESTful resource? L1

Return a tuple of data and status, or raise abort(404, message="Not found") from flask_restful.

return {"message": "created"}, 201
125What is the difference between PUT and PATCH? L1

PUT replaces the whole resource and sends all fields; PATCH changes only the supplied fields. Both are normally idempotent when used correctly.

126What is the difference between jsonify and returning a dict? L1

jsonify creates a JSON response with the right Content-Type header. Newer Flask versions also convert a returned dict to JSON automatically, and Flask-RESTful serialises returned dicts for you.

127Why is an API useful in addition to your web pages? L1

It lets other clients such as JavaScript, a mobile app or scripts use the same data in JSON, separates backend from frontend and is easy to test with curl or Postman.

128How do you test your API without a frontend? L1

Use curl, Postman or Python requests, or the Swagger UI from the OpenAPI spec. For automated checks use the Flask test client in pytest.

curl -X POST http://localhost:5000/api/books -H "Content-Type: application/json" -d '{"title":"X"}'
129Add a DELETE endpoint for a book that returns 404 when missing. L2
def delete(self, id):
    book = db.session.get(Book, id)
    if not book:
        abort(404, message="Book not found")
    db.session.delete(book); db.session.commit()
    return "", 204
130The API returns 500 for bad input instead of 400. How do you fix it? L2

Validate with reqparse (required, type, choices) or manual checks and return 400 with a message before touching the database. A 500 means an unhandled exception on the server; a client mistake should be a 4xx.

131Add a query parameter ?q= for searching books in the API. L2

Read the argument and filter.

q = request.args.get("q", "")
Book.query.filter(Book.title.ilike(f"%{q}%")).all()
132When should POST return 409 Conflict? L2

When the request is valid but conflicts with current state, such as creating a user whose unique email already exists or booking a slot already taken. Return 409 with a message rather than a generic 400 or 500.

133Nest a related list in a marshalled response. L2

Use fields.List(fields.Nested(...)).

book_fields = {"id": fields.Integer, "reviews": fields.List(fields.Nested(review_fields))}
134What is a database index and how do you create one in SQL and SQLAlchemy? L1

An index is a separate sorted structure that lets the database find rows without scanning the whole table. Create it for columns you often filter, join or sort on.

CREATE INDEX idx_booking_user ON booking(user_id);
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), index=True)
135How do you check whether a query uses an index? L1

Run EXPLAIN QUERY PLAN before the query in SQLite. SEARCH ... USING INDEX means the index is used, while SCAN means a full table scan.

EXPLAIN QUERY PLAN SELECT * FROM booking WHERE user_id = 5;
136When is an index not helpful? L1

On tiny tables, on columns with very few distinct values such as a boolean, when the query wraps the column in a function or uses a leading wildcard like LIKE '%abc', or on write-heavy tables where the maintenance cost outweighs the gain.

137What is a composite index and does column order matter? L1

A composite index covers several columns, for example (user_id, date). Order matters: it helps queries filtering on the leftmost column or on both, but not on date alone.

138How is basic search done with SQL LIKE and what are its limits? L1

WHERE title LIKE '%word%' matches a substring and ilike in SQLAlchemy is case-insensitive. A leading wildcard cannot use a normal index, so it scans every row, and there is no ranking or word-based matching.

139What is SQLite FTS5? L1

FTS5 is a SQLite extension for full-text search. You create a virtual table with the columns to search and query it with MATCH, which gives fast word-based search and ranking, unlike LIKE.

CREATE VIRTUAL TABLE docs USING fts5(title, body);
SELECT * FROM docs WHERE docs MATCH 'flask' ORDER BY rank;
140What is caching and where can an app cache? L1

Caching stores the result of expensive work so repeated requests are served faster. It can be in the browser (HTTP cache headers), in the app (Flask-Caching, memoised functions), in a store like Redis, or at a proxy or CDN. The trade-off is stale data.

141How do you use Flask-Caching? L1

Configure a cache type, then decorate a view or function with a timeout.

cache = Cache(app, config={"CACHE_TYPE": "SimpleCache"})
@app.route("/stats")
@cache.cached(timeout=60)
def stats(): ...

SimpleCache lives in process memory; use Redis for multiple workers.

142What is cache invalidation and why is it hard? L1

Invalidation means removing or updating cached data when the source changes. It is hard because you must know every write that affects the cached value. Simple strategies are short timeouts or deleting the key on updates with cache.delete.

143What are logging levels and how do you configure logging in Python? L1

From low to high: DEBUG, INFO, WARNING, ERROR, CRITICAL. A logger prints messages at or above its level.

import logging
logging.basicConfig(level=logging.INFO, filename="app.log",
                    format="%(asctime)s %(levelname)s %(message)s")
app.logger.info("User %s logged in", user_id)
144Why use logging instead of print? L1

Logging has levels, timestamps, destinations such as files, and can be switched off or tuned without editing code. In production print output may be lost, while logs support diagnosing problems later.

145How do you debug a Flask app? Mention pdb and the Flask shell. L1

Read the traceback first, add log lines, and use breakpoint() to open pdb where commands like n, s, c, p var and l step through code. The flask shell command opens a Python shell with the app context so you can try queries interactively.

146What is pagination and why use it? L1

Pagination returns results in chunks instead of loading everything. It reduces memory and response time.

page = Item.query.paginate(page=1, per_page=10)
page.items, page.has_next
147A page is slow because it lists bookings with their user names in a loop. How do you diagnose and fix it? L2

Log or count queries; this looks like an N+1 problem. Use a join or eager loading, add an index on the foreign key and paginate. Confirm with EXPLAIN QUERY PLAN and compare timings.

148Log every failed login without logging the password. L2

Log the username and time, never the secret.

app.logger.warning("Failed login for %s from %s", email, request.remote_addr)
149How would you log exceptions with the stack trace? L2

Use logging.exception or app.logger.exception inside an except block, which records the message at ERROR level together with the traceback.

try: risky()
except Exception:
    app.logger.exception("risky failed")
150Cached dashboard shows old numbers after a new booking. What do you do? L2

Delete or refresh the cache key after the write, for example cache.delete('stats') after commit, or shorten the timeout. Decide how much staleness the page can tolerate.

151What is the DOM? L1

The Document Object Model is the browser's tree representation of the HTML page. JavaScript can read and change it, for example changing text, adding elements or reacting to clicks.

152How do you select elements and change them with JavaScript? L1
const el = document.getElementById("msg");
el.textContent = "Saved";
document.querySelector(".btn").classList.add("active");

querySelector accepts any CSS selector; textContent is safer than innerHTML for user text.

153How do you handle events? L1

Attach a listener with addEventListener.

document.getElementById("btn").addEventListener("click", () => {
  alert("clicked");
});

event.preventDefault() stops default behaviour such as a form submit.

154What is fetch and how do you call an API with it? L1

fetch makes HTTP requests from JavaScript and returns a Promise.

fetch("/api/books")
  .then(r => r.json())
  .then(data => console.log(data));
155Show a POST request with JSON using fetch. L1
fetch("/api/books", {
  method: "POST",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({title: "X"})
}).then(r => r.json());

Without the Content-Type header, request.get_json() on the server fails.

156What are Promises and async/await? L1

A Promise represents a value that will arrive later. async/await is syntax to write asynchronous code in a sequential style.

async function load() {
  const r = await fetch("/api/books");
  const data = await r.json();
}
157Does fetch reject on a 404 or 500 response? L1

No. It rejects only on network failure. Check response.ok or response.status yourself and handle errors explicitly.

158What is the difference between let, const and var? L1

const cannot be reassigned, let is block-scoped and reassignable, var is function-scoped and hoisted, which causes surprises, so prefer const and let.

159What is the difference between == and === in JavaScript? L1

== converts types before comparing, so 0 == '0' is true. === compares without conversion, so it is false. Prefer ===.

160When would you use JavaScript in a MAD1 project? L1

For small enhancements: client-side validation, confirming deletes, live search filter, fetching JSON from your API to update part of a page without reload, or drawing charts. The core logic and validation remain on the server.

161Add a confirm dialog before deleting. L2
<form method="post" action="/delete/5" onsubmit="return confirm('Delete this item?');">
  <button class="btn btn-danger">Delete</button>
</form>
162Live filter a table as the user types. L2
document.getElementById("q").addEventListener("input", e => {
  const q = e.target.value.toLowerCase();
  document.querySelectorAll("tbody tr").forEach(tr => {
    tr.style.display = tr.textContent.toLowerCase().includes(q) ? "" : "none";
  });
});
163Your fetch to the Flask API returns 400. How do you debug in the browser? L2

Open the Network tab, click the request and check the request payload, Content-Type header and the response body, which usually contains the error message. Also check the Console for JavaScript errors.

164Why can JavaScript fail to find an element and return null? L2

The script may run before the element exists, so place it at the end of body or use defer, or the id or selector may be misspelled. Check the Elements panel for the real id.

165What is the difference between authentication and authorization? L1

Authentication verifies who you are, for example by password. Authorization decides what you are allowed to do, for example whether you can open the admin page. Both are needed.

166How should passwords be stored? L1

Only as salted hashes produced by a slow hashing function, never plain text. Werkzeug provides this.

from werkzeug.security import generate_password_hash, check_password_hash
h = generate_password_hash("secret")
check_password_hash(h, "secret")
167Why can hashes not be reversed, and why use a salt? L1

A hash is a one-way function, so you verify by hashing the input again and comparing. A random salt per password makes identical passwords produce different hashes and defeats precomputed rainbow tables.

168What is session-based login in Flask? L1

After checking credentials you store the user id in session; later requests carry the signed cookie so the server knows who the user is. Logout clears the session with session.clear().

169What is role-based access control (RBAC)? L1

Users are assigned roles such as admin, customer or sponsor, and each route or action is allowed only for particular roles. It is enforced in the backend with decorators, not by merely hiding links.

170What is Flask-Security and what does it provide? L1

Flask-Security (Flask-Security-Too) adds registration, login, logout, password hashing, roles and role-protected routes on top of Flask-Login. You define User and Role models and use decorators like @login_required and @roles_required('admin').

171What is XSS and how is it prevented? L1

Cross-site scripting is injecting script into a page so it runs in another user's browser. Prevent it by escaping output, which Jinja2 does by default, avoiding |safe on user input and avoiding innerHTML with user text.

172What is CSRF and how do you defend against it? L1

Cross-site request forgery makes a logged-in user's browser send an unwanted request to your site. Defend with a secret per-form token (Flask-WTF CSRFProtect), SameSite cookies and using POST for state changes.

173What is SQL injection and how do you prevent it? L1

It is when user input is concatenated into SQL and changes the query, for example ' OR 1=1 --. Use the ORM or parameterised queries so input is only data.

174What is IDOR and how does your app avoid it? L1

Insecure direct object reference means a user changes an id in the URL to access someone else's record. Always check ownership on the server by filtering with the logged-in user's id, and return 403 or 404 otherwise.

175Why is client-side validation not enough? L1

Anyone can bypass the browser using dev tools, curl or a script. The server must validate every input for type, length, uniqueness and permission.

176Which cookie flags improve security? L1

HttpOnly stops JavaScript reading the cookie, Secure sends it only over HTTPS and SameSite limits cross-site sending. In Flask: SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SECURE, SESSION_COOKIE_SAMESITE.

177How do you keep secrets like SECRET_KEY out of code? L1

Read them from environment variables or an untracked config file and add that file to .gitignore.

app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
178A normal user types /admin in the URL and sees the page. Fix it. L2

The route lacks a server-side role check. Add a decorator that verifies the logged-in role and aborts with 403, and also test it manually and in pytest.

@app.route("/admin")
@login_required
@roles_required("admin")
def admin(): ...
179Change the stored password hashing so existing users keep working. L2

Hash on registration with the new method and verify with check_password_hash, which reads the method from the stored hash. For stronger scheme migration, rehash the password on the next successful login.

180Why is session['user'] = user_id safer than a cookie holding the plain user id? L2

The Flask session cookie is signed, so tampering invalidates it, while a plain id cookie could be edited to impersonate someone. Still, the session is only signed, so do not store sensitive data in it.

181What is the difference between unit tests and functional tests? L1

A unit test checks one small piece such as a function in isolation. A functional (integration) test exercises a feature through several layers, for example posting a form through the test client and checking the response and database.

182What is pytest and how do you write a test? L1

pytest finds functions named test_* and uses plain assert statements.

def test_add():
    assert 1 + 2 == 3

Run with pytest -v.

183What is a fixture? L1

A fixture is reusable setup and teardown code injected into tests by name. A typical one creates an app and an empty test database.

@pytest.fixture
def client():
    app = create_app({"TESTING": True, "SQLALCHEMY_DATABASE_URI": "sqlite://"})
    with app.app_context():
        db.create_all()
        yield app.test_client()
184How do you test a Flask route? L1

Use the test client and assert on status code and content.

def test_home(client):
    r = client.get("/")
    assert r.status_code == 200
    assert b"Welcome" in r.data
185How do you test a POST with login? L1
def test_login(client):
    r = client.post("/login", data={"email": "a@b.com", "password": "x"},
                    follow_redirects=True)
    assert r.status_code == 200

follow_redirects=True follows the 302 to the final page.

186Why use a separate test database? L1

Tests must not damage real data and must start from a known state so they are repeatable. In-memory SQLite (sqlite://) or a temporary file is created and thrown away for each test.

187What is code coverage and how do you measure it? L1

Coverage is the percentage of lines your tests execute. Run coverage run -m pytest then coverage report, or use pytest --cov=app. High coverage does not prove correctness but low coverage shows untested code.

188What edge cases should you test in a booking app? L1

Duplicate registration, empty or invalid fields, nonexistent ids, double booking the same slot, unauthorised access to another user's data and admin routes, and deleting a parent with children.

189What is a test of a REST API like? L1

Send requests with the test client and check status code and JSON.

r = client.post("/api/books", json={"title": "X"})
assert r.status_code == 201
assert r.get_json()["title"] == "X"
190What is manual testing and when is it still needed? L1

Manual testing means trying scenarios yourself in a browser. It finds layout and usability problems that automated tests miss, but is slow and not repeatable, so combine with automated tests.

191A test passes alone but fails when run with others. Why? L2

Tests share state such as the database. Make each test use a fresh fixture with its own app and tables, and avoid depending on test order.

192Test that a normal user gets 403 on an admin route. L2
def test_admin_forbidden(client, login_user):
    r = client.get("/admin")
    assert r.status_code == 403
193Your test hits 'working outside of application context'. Fix it. L2

The code touching the database needs an app context. Create the app in the fixture and use with app.app_context(): around setup and queries.

194What is the doctype and why is it needed? L1

<!DOCTYPE html> tells the browser to use standards mode for HTML5. Without it browsers may render in quirks mode with older, inconsistent behaviour.

195What are the new HTML5 semantic elements and form features? L1

Semantic elements include header, nav, main, section, article, aside and footer. Forms gained types like email, date, number, range and attributes such as required, placeholder and pattern. HTML5 also has audio, video and canvas.

196What is accessibility (a11y) and why does it matter? L1

Accessibility means people with disabilities, for example screen reader or keyboard-only users, can use the site. It matters for fairness, legal reasons and also improves usability and SEO for everyone.

197How do you make a page accessible? L1

Use semantic tags, give images alt text, connect labels to inputs, keep sufficient colour contrast, ensure everything works via keyboard with visible focus, and use ARIA only when native elements are not enough.

198How do you associate a label with an input? L1

Use for matching the input's id, or wrap the input in the label.

<label for="email">Email</label>
<input type="email" id="email" name="email">

Clicking the label focuses the input, and screen readers announce it.

199What is the alt attribute? L1

It provides text for an image when it cannot be seen, read out by screen readers and shown if the image fails to load. Decorative images get an empty alt="".

200What is a character encoding and why use UTF-8? L1

Encoding maps characters to bytes. UTF-8 can represent every Unicode character, is backward-compatible with ASCII and is the web standard. Declare it with <meta charset="utf-8"> so non-English text displays correctly.

201What is the difference between ASCII and Unicode? L1

ASCII has 128 characters, mainly English letters and symbols. Unicode assigns a code point to characters of all scripts and emoji; UTF-8 is one way to encode those code points into bytes.

202What are HTML entities? L1

Entities represent reserved or special characters, for example &lt; for <, &gt;, &amp; for & and &quot;. They stop the browser treating text as markup and are what Jinja2 autoescaping outputs.

203What are ARIA attributes? L1

ARIA attributes such as role, aria-label and aria-live add accessibility information to elements where plain HTML cannot express it. The first rule of ARIA is to use a native element like button if one exists.

204What is URL encoding? L1

Special characters in URLs are replaced with percent codes, for example a space becomes %20 and & becomes %26. Query values should be encoded, and Flask decodes them in request.args.

205A screen reader reads 'button button' for an icon-only button. Fix it. L2

Give the button an accessible name with aria-label or visible hidden text.

<button aria-label="Delete item"><i class="bi bi-trash"></i></button>
206Hindi or accented text shows as garbled characters. What do you check? L2

Ensure the HTML has <meta charset="utf-8">, the file is saved as UTF-8, and the response Content-Type charset is UTF-8. Also check that the database and any file reads use UTF-8.

207Why should a form error message not rely on colour alone? L2

Colour-blind users or screen readers may miss it. Add text such as 'Error: email required', an icon, and link it to the field with aria-describedby.

208What is WSGI? L1

WSGI (Web Server Gateway Interface) is the standard Python interface between web servers and applications. Flask is a WSGI app, so servers like gunicorn can call it. Flask's built-in server is only for development.

209Why not use flask run in production? L1

The development server is single-purpose, not hardened and weak under load. Use a production WSGI server such as gunicorn with several workers, normally behind nginx.

gunicorn -w 4 -b 127.0.0.1:8000 app:app
210What does nginx do in front of gunicorn? L1

It acts as a reverse proxy: handles HTTPS, serves static files efficiently, buffers slow clients and forwards dynamic requests to gunicorn. This improves security and performance.

211What is a virtual environment and why use one? L1

A virtual environment is an isolated Python installation per project so package versions do not clash.

python -m venv venv
source venv/bin/activate    # Windows: venv\Scripts\activate
pip install -r requirements.txt
212What is requirements.txt? L1

It lists the project's dependencies so anyone can reproduce the environment. Generate it with pip freeze > requirements.txt and install with pip install -r requirements.txt.

213What is Docker and why use it? L1

Docker packages the app with its dependencies into an image that runs the same on any machine as a container. It removes 'works on my machine' problems and simplifies deployment.

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app:app"]
214What is the difference between a Docker image and a container? L1

An image is the read-only template built from the Dockerfile; a container is a running instance of an image.

215What are the basic Git commands you use? L1
git init
git add .
git commit -m "message"
git branch feature
git checkout feature
git push origin main

Git tracks changes so you can see history, work on branches and undo mistakes.

216What belongs in .gitignore? L1

Files that should not be versioned: the virtual environment, __pycache__, .db files, uploaded files, .env and secrets, and editor settings.

217How do you manage configuration differences between development and production? L1

Use separate config classes or environment variables for debug flag, database URI and secret key, and select by environment. Never commit production secrets.

218How would you deploy a Flask app to a server in outline? L1

Copy the code, create a virtualenv, install requirements, set environment variables, run migrations, start gunicorn under a process manager such as systemd, and configure nginx as the reverse proxy with HTTPS.

219The project works on your laptop but fails on the examiner's machine. What do you check? L2

Missing or version-mismatched dependencies (update requirements.txt), hard-coded absolute paths, a database that is not created at startup, a missing environment variable, or OS-specific path separators. Provide clear run instructions and test them in a fresh virtualenv.

220You accidentally committed a .db file and a secret. What now? L2

Remove the file from tracking with git rm --cached, add it to .gitignore and commit. A secret that was pushed must be treated as leaked: rotate it, since history still contains it.

221Run the app on a different port or host. L2
flask run --port 5001 --host 0.0.0.0

Or app.run(port=5001). Binding to 0.0.0.0 exposes the dev server to the network, so do it only deliberately.

222Give a short overview of your project and its users. L1

State the problem it automates, name the roles such as admin and user, and list the main features in one breath, for example registration, browsing, booking and an admin dashboard. Then name the stack: Flask, Jinja2, SQLite with SQLAlchemy, Bootstrap. Keep it under thirty seconds and let them ask for detail.

223Why did you choose SQLite? L1

SQLite is serverless, needs no setup, ships with Python and is a single file, so the examiner can run the project anywhere. For the scale of this project, with modest concurrent writes, it is enough. If it grew I would move to PostgreSQL by changing the connection URI, as SQLAlchemy abstracts the database.

224Why Flask and not Django? L1

Flask is lightweight and explicit, so I understand every part of the request flow: routing, templates and database. It suits a small to medium project and the course teaches it. Django offers an admin and ORM out of the box, but that hides things I wanted to learn.

225Explain your database schema and why you designed it that way. L1

Name each table, its primary key and the relationships: for example User 1-N Booking N-1 Slot. Explain that repeated information is stored once and referenced by foreign keys, which follows third normal form, and mention the constraints you added like unique and not null.

226How does your app prevent a user from seeing another user's data? L1

Every protected route requires login and every query is filtered by the logged-in user's id from the session, never by an id trusted from the URL alone. For a record opened by id, I check record.user_id == current_user.id and abort with 403 or 404 otherwise. I tested this by trying another user's URLs.

b = Booking.query.get_or_404(id)
if b.user_id != session["user_id"]:
    abort(403)
227How do you stop duplicate bookings or registrations? L1

At two levels. In the database a UniqueConstraint on the columns that must be unique, such as (user_id, slot_id) or email, guarantees it even with two simultaneous requests. In code I check first for a friendly message and also catch IntegrityError and roll back.

228How is the admin created and how is admin access protected? L1

The admin is created programmatically at first startup if none exists, so there is no public admin signup. Admin routes are guarded by a role check that returns 403 for others, in the backend, not just by hiding menu links.

229Where do you validate input and why in both places? L1

In the browser with required, types and patterns for quick feedback, and on the server for every field: presence, type, length, uniqueness and permission. The server is the authority because browser checks can be bypassed.

230How is search implemented in your app? L1

Describe your real implementation: typically a filter with ilike on name or category using a query parameter, optionally ordered and paginated. Say that if data grew, I would add an index or use SQLite FTS5.

231Walk me through one feature end to end. L1

Pick one, for example booking: the user clicks a button which is a form POST to a route; the route checks login, validates input, creates a Booking through SQLAlchemy, commits and redirects to the bookings page; the template loops over the user's bookings. Name the template, route function and model at each step.

232How do you handle deleting a record that other records refer to? L1

Either cascade the delete to children with cascade='all, delete-orphan', block deletion while children exist with a friendly message, or soft delete with a flag so history is kept. I chose whichever fits the business rule, for example soft delete for items that appear in past bookings.

233What happens if a user submits invalid data or the database fails? L1

Invalid input is caught by validation, which re-renders the form with a flash message. A database error is caught, the session rolled back so nothing half-saved remains, and a friendly message or error page is shown while the details go to the log.

234What would you improve with more time? L1

Pagination and caching for large lists, automated pytest tests, a proper token-based REST API with documentation, CSRF protection if not present, migrations with Flask-Migrate, and deployment with gunicorn and Docker.

235What was the hardest part and how did you solve it? L1

Give a specific example, such as modelling relationships or preventing double booking. Describe the symptom, how you debugged it using the traceback, logging or the Flask shell, and the fix. A concrete story shows you wrote the code.

236Did you use external help or AI? Can you explain every line? L1

Answer honestly. Say what you took from documentation, tutorials or tools, and that you reviewed, tested and understand all code. Then be ready to explain any file you are shown, because that is what the viva checks.

237Show me where in your code a user is authenticated, and change the redirect after login live. L2

Open the login route, point to the credentials check and the session assignment, then edit the redirect target.

return redirect(url_for("user_dashboard"))   # was "home"

Save, restart if needed, and demonstrate the new behaviour.

238Add a new field to a model in your project and show it in the UI. L2

Add the column to the model, apply the schema change (migration, or recreate the dev database), add the input to the form, read it with request.form, and display it in the template. Mention that existing rows will have a default or NULL.

239How would your project behave with two users booking the last slot at the same moment? L2

Without protection both could succeed. A unique constraint or a conditional update inside one transaction makes the database accept only one and raise IntegrityError for the other, which I catch and report as slot taken. Checking in Python alone is not enough because of the race.

240Add a new route /hello/<name> that greets the user. L2
@app.route("/hello/<name>")
def hello(name):
    return f"Hello, {name}!"

The <name> part of the URL arrives as the function argument. In real pages use a template so the name is escaped.

241Add a column 'phone' to the User model and show it on the profile page. L2
phone = db.Column(db.String(15))

Run a migration or recreate the database in development, add an input named phone in the form, save it with user.phone = request.form['phone'], and show {{ user.phone }} in the template.

242Change the dashboard to show only the latest 5 records. L2
items = Item.query.order_by(Item.created_at.desc()).limit(5).all()

order_by(...desc()) puts the newest first and limit(5) keeps five.

243Add a search box that filters records by name. L2
q = request.args.get("q", "")
items = Item.query.filter(Item.name.ilike(f"%{q}%")).all()
<form method="get"><input name="q" value="{{ request.args.get('q','') }}"><button>Search</button></form>
244Make the delete button work with POST rather than a link. L2

A link makes a GET, which should not change data. Use a form.

<form method="post" action="{{ url_for('delete_item', id=item.id) }}"><button>Delete</button></form>
@app.route("/delete/<int:id>", methods=["POST"])
245Disable a user (block) without deleting them and stop them logging in. L2

Add is_active = db.Column(db.Boolean, default=True) and check it in the login route.

if not user.is_active:
    flash("Account blocked"); return redirect(url_for("login"))

Blocking sets the flag to False and commits.

246Show a count of bookings per user on the admin page. L2
rows = (db.session.query(User.name, func.count(Booking.id))
        .outerjoin(Booking).group_by(User.id).all())

In the template loop {% for name, n in rows %}.

247Add a unique constraint on (user_id, slot_id) and handle the error. L2
__table_args__ = (db.UniqueConstraint("user_id", "slot_id"),)

Then catch IntegrityError around commit, call db.session.rollback() and flash a message.

248Change the Bootstrap layout so cards show 3 per row on large screens and 1 on phones. L2
<div class="row">
  {% for c in cards %}
  <div class="col-12 col-lg-4 mb-3"><div class="card">...</div></div>
  {% endfor %}
</div>
249Add a flash message after a successful save and display it on the page. L2
flash("Saved successfully", "success")
return redirect(url_for("list_items"))

Show it in base.html with a loop over get_flashed_messages(with_categories=true) and a Bootstrap alert.

250Add pagination of 10 items per page. L2
page = request.args.get("page", 1, type=int)
p = Item.query.paginate(page=page, per_page=10)

In the template loop p.items and link to url_for('items', page=p.next_num) when p.has_next.

251Add a JSON endpoint /api/items that returns all items. L2
@app.route("/api/items")
def api_items():
    return jsonify([{"id": i.id, "name": i.name} for i in Item.query.all()])
252Sort a table by price when the user clicks a link. L2

Pass ?sort=price and map it to a column.

sort = request.args.get("sort", "name")
col = {"name": Item.name, "price": Item.price}.get(sort, Item.name)
items = Item.query.order_by(col).all()

Using a whitelist prevents arbitrary column names.

253Show a custom 404 page. L2
@app.errorhandler(404)
def nf(e):
    return render_template("404.html"), 404

Return the status code 404 with the page so browsers and tools see a real not found.

254Hash the password at registration and verify on login. L2
user = User(email=email, password=generate_password_hash(pw))
...
if user and check_password_hash(user.password, pw): session["user_id"] = user.id

The password column must be long enough, for example 255 characters.

255Log out so that the back button does not show a protected page. L2

Clear the session and redirect, and add no-store headers on protected pages.

session.clear()
@app.after_request
def nocache(r): r.headers["Cache-Control"] = "no-store"; return r
256Add a created_at timestamp to a model. L2
from datetime import datetime, timezone
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))

Pass a function so the time is computed per row, not once at import.

257Prevent a past date being booked. L2

Validate on the server.

d = datetime.strptime(request.form["date"], "%Y-%m-%d").date()
if d < date.today():
    flash("Date must be today or later"); return redirect(url_for("book"))

Add min on the date input too for the browser.

258Add an index on a column and prove it is used. L2
name = db.Column(db.String(80), index=True)

Then run EXPLAIN QUERY PLAN SELECT * FROM item WHERE name = 'x'; and look for USING INDEX.