DEV Community

Cover image for Applying MVC Architecture in Python: Building an Automated Certificate Generator

Applying MVC Architecture in Python: Building an Automated Certificate Generator

Co-authored with Liarrah Lambayao (@ldlambayao)

Have you ever tried to organize your files by placing all of them in a single folder?🤔

Oh, everything quickly became messy and harder to manage. The same way works with software too. When all of the code is placed in a single system, it becomes harder for the developer to manage, maintain, edit and fix the code. Even the possibility of scaling and improving it would become more difficult. This is where MVC Architecture comes in, a clean and structured way to keep everything organized and manageable.

MVC Architecture is a software design that is frequently used by developers in the frontend and backend.

What is MVC Architecture?

MVC stands for Model-View-Controller. It concerns making things easier to manage, maintain, and make the code clear and organized. Projects without MVC are like putting all of your eggs in a single basket; if a single part of the code malfunctions, the whole system is at risk.

Applying MVC would make things easier as it separates the system into three different components/roles: Model, View, and Controller.

  • The Model holds and manages the data, and in this case, the recipient’s information and certificate data. If the data changes, the model will tell the view about it.

  • View is what the user sees in the application. It displays the data from the Model and updates when the underlying data changes.

  • The Controller is what ties everything together. It processes user input and connects the Model and View components. It receives the user input and decides what to do with it.

MVC Diagram

Project Overview

To demonstrate how MVC is applied in practice, this article walks through the development of a simple automated certificate generator in Python. As certificates are an essential part of recognizing accomplishments and creating them manually can be tedious, this project reads information such as the date, name, etc., from a .txt file and generates certificates accordingly.

Although automating certificates is a common beginner-friendly project that focuses on getting the task done with Python libraries, this article takes a different approach by highlighting how the Model-View-Controller (MVC) architecture can be applied to organize code and build a cleaner, more modular solution.

This article takes an example from a pre-existing repository in GitHub and refactors it into a Model–View–Controller structure. By restructuring the original implementation, this project demonstrates how the separation of concerns can improve clarity, modularity, and scalability.

🔗github repository by gunarakulangunaretnam

  • Current Structure of Repository
certificate generator/
|
|----generated-certificates/    # output directory for generated certificate
|
|----run.py                     # python script
|
|----certificate-template.jpg   # certificate template
|
|----name-data.txt/.xlsx        # file that contains recipients
Enter fullscreen mode Exit fullscreen mode
  • Python Script
import os
import cv2

list_of_names = []


def delete_old_data():
   for i in os.listdir("generated-certificates/"):
      os.remove("generated-certificates/{}".format(i))


def cleanup_data():
   with open('name-data.txt') as f:
      for line in f:
          list_of_names.append(line.strip())


def generate_certificates():

   for index, name in enumerate(list_of_names):
      certificate_template_image = cv2.imread("certificate-template.jpg")

      cv2.putText(certificate_template_image, 
name.strip(), (815,1500), cv2.FONT_HERSHEY_SIMPLEX, 
5, (0, 0, 250), 5, cv2.LINE_AA)

      cv2.imwrite("generated-certificates/{}.jpg".format(name.strip()), certificate_template_image)

      print("Processing {} / {}".format(index + 1,len(list_of_names)))

def main():
   delete_old_data()
   cleanup_data()
   generate_certificates()



if __name__ == '__main__':
   main()

Enter fullscreen mode Exit fullscreen mode

As can be seen, the Python script works, but everything is placed under a single script, from reading the information to generating certificates, and controlling the program flow, all at once. While it works perfectly, it can be difficult to manage as the program grows; the same goes for any other project.

For example, if we wish to modify the certificate layout, adjust the position of text, or change the data source from a text file to an Excel file, among other things, to do these, we would need to edit the same script. This increases the errors and makes the code longer and more complex than it should be, making it harder to debug, test, and maintain. This is where MVC comes in handy. By separating the data handling, the presentation, and lastly the program logic, the code becomes organized and ready for more scalable implementations.

Thus, this is how we can implement MVC using a new project structure

  • Project Structure using MVC
certificate generator /
|
|--models/
|   |--model.py
|
|--views/
|   |--view.py
|
|--controllers/
|   |--controller.py
|
|--run.py
|
|--name-data.txt
|
|--certificate-template.jpg
|
|--generated-certificates/
Enter fullscreen mode Exit fullscreen mode

Next, we can now define the model, implement the view, and create the controller before putting it all together.

a. Define the Model

import cv2
import os

# receives list of names from controller and is responsible for rendering the certificates
def generate_certificates(list_of_names):
    for index, name in enumerate(list_of_names):
        certificate_template_image = cv2.imread("certificate-template.jpg")

        cv2.putText(
            certificate_template_image,
            name,
            (688,707), # customize position to move the text to your liking
            cv2.FONT_HERSHEY_SIMPLEX,# customize font with another OpenCV font
            5,# customize font size 
            (0, 0, 0), # customize color
            5, # customize thickness
            cv2.LINE_AA
        )

        cv2.imwrite(
            os.path.join("generated-certificates", f"{name}.jpg"),
            certificate_template_image
        )

        print(f"Processing {index + 1} / {len(list_of_names)}")

Enter fullscreen mode Exit fullscreen mode

In this version, list_of_names is now used as a local variable, which enforces proper data flow.

b. Implement the View

import cv2
import os

# receives list of names from controller and is responsible for rendering the certificates
def generate_certificates(list_of_names):
    for index, name in enumerate(list_of_names):
        certificate_template_image = cv2.imread("certificate-template.jpg")

        cv2.putText(
            certificate_template_image,
            name,
            (688,707), # customize position to move the text to your liking
            cv2.FONT_HERSHEY_SIMPLEX,# customize font with another OpenCV font
            5,# customize font size 
            (0, 0, 0), # customize color
            5, # customize thickness
            cv2.LINE_AA
        )

        cv2.imwrite(
            os.path.join("generated-certificates", f"{name}.jpg"),
            certificate_template_image
        )

        print(f"Processing {index + 1} / {len(list_of_names)}")

Enter fullscreen mode Exit fullscreen mode

Similar to model.py, we no longer depend on the global variable list_of_names. Instead, the function now receives data explicitly and no longer depends on external state.

You may also customize the input text to your liking from its position, font, font size, font color, and thickness. Feel free to explore and tweak as much as you can!

  • How to identify the accurate position of your text
  1. Open your certificate template with MS Paint
  2. Point your cursor to the space you wish to fill with your text.
  3. Follow the coordinates found at the lower left.

Certificate Template w/ MS Paint

c. Create the Controller

from models.model import delete_old_data, cleanup_data
from views.view import generate_certificates

def run_application():
    delete_old_data()
    list_of_names = cleanup_data()
    generate_certificates(list_of_names)

Enter fullscreen mode Exit fullscreen mode

Similar to the new versions of model.py and view.py, there is no global variable used anymore, and the data is passed explicitly. Making it a better design.

d. Putting it all together

from controllers.controller import run_application  

if __name__ == "__main__":
    run_application()
Enter fullscreen mode Exit fullscreen mode

Acts as the entry point of the program.

An alternative is to use .xlsx files, which might be the most practical and efficient way, especially when handling a large number of recipients. This becomes particularly useful in events where information is often stored in spreadsheets.

To do this, we only have to modify model.py, a perfect scenario of why the separation of concerns is beneficial and how a well-structured architecture makes applications easier to maintain, extend, and eventually adapt to new changes.

  • Modifying your data source
  1. Install Pandas (if not yet installed)

    py -m pip install pandas openpyxl
    
  2. Modify your model.py, assuming your Excel file looks like this

    name date
    person1 date
    person2 date
    ... ...

    name-data.xlsx

  3. Update models/model.py to this:

    import os
    import pandas as pd
    
    def delete_old_data():
        for i in os.listdir("generated-certificates/"):
            os.remove(os.path.join("generated-certificates/", i))
    
    def cleanup_data():
        # Read Excel file
        df = pd.read_excel("name-data.xlsx")
    
        # Convert each row into a dictionary
        records = df.to_dict(orient="records")
    
        return records
    




    What changed?
    ✅ We added pandas
    ✅ We modified the cleanup_data function to read .xlsx, convert into a list of dictionaries

  4. Modify view.py as needed

    import cv2
    import os
    
    def generate_certificates(records):
        for index, record in enumerate(records): # used records (dictionary)
            certificate_template_image = cv2.imread("certificate-template.jpg")
    
        # dictionary key access
            name = str(record["name"])
            date = str(record["date"])
    
        # name
            cv2.putText(
                certificate_template_image,
                name,
                (688, 707),
                cv2.FONT_HERSHEY_SCRIPT_COMPLEX,
                3,
                (0, 0, 0),
                3,
                cv2.LINE_AA
            )
    
            # date
            cv2.putText(
                certificate_template_image,
                date,
                (407, 1104),
                cv2.FONT_HERSHEY_SIMPLEX,
                1,
                (0, 0, 0),
                3,
                cv2.LINE_AA
            )
    
            cv2.imwrite(
                os.path.join("generated-certificates", f"{name}.jpg"),
                certificate_template_image
            )
    
            print(f"Processing {index + 1} / {len(records)}")
    




    What changed?
    ✅ Used records instead of list_of_names
    ✅ Explicit string conversion is necessary for the use of OpenCV
    ✅ Accommodated additional information so that the view renders multiple pieces of information

    Thanks to the MVC-inspired structure, switching from a .txt file to an .xlsx file becomes less of a hassle with the help of the model that keeps data isolated from the rest, thus, preserving the rest of your application’s logic and layout.


Testing

  • Testing with txt files
  1. Simply run the program in your terminal with
python run.py 
or
py run.py 
Enter fullscreen mode Exit fullscreen mode

And let’s say you have 10 recipients of your certificate.

10 recipients

Check the generated-certificates folder, and it should contain all of the certificates of the recipients.

generated-certificates folder

generated-certificates

Example:
Aidan Gilmore

  • Testing with xlsx file
  1. Follow the same steps, and it should produce the same set of certificates, but this time, with date included.

Example:
Aidan Gilmore

How MVC Made the Project Clear

Using MVC made the project much easier to understand, manage, and maintain. By separating the system into three parts: one handles the data such as the recipient’s name, date, and event details, another part controls the certificate layout and design—how it looks on the screen, and another part manages the user’s actions and ties everything together. Because of the clear separation of roles between them, the system considerably becomes easier to maintain and manage.

While this is a clean, beginner-friendly, MVC-inspired modular architecture, its implementation does not represent a full framework-level MVC with reactive views or even user-driven controllers. Yet, this project applies the very core principle of MVC, which is the separation of concerns. By dividing its data handling, presentation logic, and program control into separate modules, the project becomes more modular, maintainable, and scalable.

Furthermore, debugging became simpler as separation of components means the errors are confined to specific components rather than affecting the entire system. That’s why MVC makes it easier to scale and improve projects in the future.

Hmm, what could possibly go wrong if you put all of your eggs in one basket? 🤔

Not applying MVC would mean that all of the components are in one place; code for interface, logic, and data handling is mixed together. This would signify that in the case of an error, the entire system could be affected, making debugging more difficult. Additionally, making changes in one part of the code would be difficult, as doing so might unintentionally break other parts. This makes the system harder to scale or expand. Without MVC, every part of the code might need adjustments. That would be very tiring…😔

Conclusion

By applying MVC in building an automated certificate generator using python, it ensures that the project will be maintainable, reusable, and easy to scale and expand in the future.

Remember not to put all of your eggs in one basket! 🥚✨

It’s a wrap!🎉 Now you know that understanding MVC would not only help developers structure projects cleanly, but also lay the foundation for advanced design patterns such as MVVM and MVP, which are all principles adopted in clean architecture to create maintainable and scalable software.

So, don’t stop here. Challenge yourself to go beyond the basics—feel free to customize it further, you can enhance it by adding GUI or even deploying it as a web application. Keep innovating, keep refining, and keep learning! 🌟

Happy building! 🚀

References
https://www.codecademy.com/article/mvc-architecture-model-view-controller

Top comments (0)