Django app for building dashboards using raw SQL queries

Overview

django-sql-dashboard

PyPI Changelog License

Django app for building dashboards using raw SQL queries

Brings a useful subset of Datasette to Django.

Currently only works with PostgreSQL.

This is very early alpha. You should not yet trust this code, especially with regards to security. Do not run this in production (yet)!

Installation

Install this library using pip:

$ pip install django-sql-dashboard

Usage

Add "django_sql_dashboard" to your INSTALLED_APPS.

Add the following to your urls.py:

from django.urls import path
from django_sql_dashboard.views import dashboard, dashboard_index

urlpatterns = [
    path("dashboard/", dashboard_index, name="django_sql_dashboard-index"),
    path("dashboard/<slug>/", dashboard),
    # ...
]

Now visit /dashboard as a staff user to start trying out the dashboard.

Screenshot

Django_SQL_Dashboard screenshot

Development

To contribute to this library, first checkout the code. Then create a new virtual environment:

cd django-sql-dashboard
python -mvenv venv
source venv/bin/activate

Or if you are using pipenv:

pipenv shell

Now install the dependencies and tests:

pip install -e '.[test]'

To run the tests:

pytest
Comments
  • Run tests against multiple PostgreSQL versions

    Run tests against multiple PostgreSQL versions

    It would be great to have the GitHub Actions workflow run tests against multiple PostgreSQL versions to catch this kind of thing in the future https://github.com/simonw/django-sql-dashboard/blob/main/.github/workflows/test.yml

    Originally posted by @simonw in https://github.com/simonw/django-sql-dashboard/issues/138#issuecomment-873649061

    ci 
    opened by simonw 15
  • If a column is called

    If a column is called "group" (or other reserved words) the example link to the table doesn't work

    Clicking this link:

    SQL__select_count____from_availability_tag_____select_id__name__notes__disabled__previous_names__slug__group_from_availability_tag

    Links to this SQL which throws an error:

    select id, name, notes, disabled, previous_names, slug, group from availability_tag
    

    Error:

    syntax error at or near "group" LINE 1: ... id, name, notes, disabled, previous_names, slug, group from... ^

    bug 
    opened by simonw 11
  • Add a Docker Compose setup for development.

    Add a Docker Compose setup for development.

    This attempts to add support for contributing to the project using Docker Compose, as mentioned in https://github.com/simonw/django-sql-dashboard/issues/120#issuecomment-859152991. My hope is that this will make it easier for contributors (or at least, ones familiar with Docker Compose) to get up and running.

    Specifically, it does the following:

    • Runs test_project interactively on port 8000
    • Runs the Sphinx auto-reloading server on port 8001
    • Runs a Postgres database that test_project uses

    Instructions

    Note: These instructions are now out of date; see this PR's contributing.md for up-to-date instructions.

    Setting up the Docker Compose environment can be accomplished with:

    docker-compose build
    
    # Wait a few seconds after running this, to give Postgres time
    # to create the initial account and database.
    docker-compose up -d db
    
    docker-compose run app python manage.py migrate
    docker-compose run app python manage.py createsuperuser
    

    Once you've done that, you can run:

    docker-compose up
    

    This will start up both the test project and the Sphinx documentation server. If you only want to start one of them, you can use docker-compose up app to start up only the test project, or docker-compose up docs to start up only the documentation server.

    You will probably want to visit http://localhost:8000/admin/ to log in as your newly-created superuser, and then visit http://localhost:8000/dashboard/ to tinker with the dashboard UI.

    If you want to run the test suite, you can run:

    docker-compose run app pytest
    

    To do

    • [x] Figure out if this is actually something @simonw wants merged into the codebase (it's fine if not; since this PR solely adds files to the repo and doesn't change existing files, I can always use this stuff separately for my personal use)
    • [x] Add documentation about all this to contributing.md.
    • [x] See if we can automate the initial manage.py migrate.
    • [x] See if we can dynamically use the UID of the current user rather than hard-coding it to 1000--or at least, provide a way to specify a custom value for UID/GID.
    opened by toolness 11
  • Export ALL results for a query as CSV/TSV

    Export ALL results for a query as CSV/TSV

    For any given SQL query the ability to export the entire resultset as CSV would be incredibly useful.

    It could be expensive, so we would want to restrict it to only specific trusted users.

    enhancement 
    opened by simonw 11
  • Run tests against a read-only

    Run tests against a read-only "dashboard" database connection

    Related to #16. I want to encourage using a separate "dashboard" database alias which is configured something like this:

    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.postgresql_psycopg2",
            "NAME": "mydb",
        },
        "dashboard": {
            "ENGINE": "django.db.backends.postgresql_psycopg2",
            "NAME": "mydb",
            "OPTIONS": {"options": "-c default_transaction_read_only=on -c statement_timeout=100"},
        },
    }
    

    I want to write the tests against this - but I'm running into some trouble because the test framework isn't designed to handle read-only database connections like this. I'm seeing errors like this:

    Got an error creating the test database: cannot execute CREATE DATABASE in a read-only transaction

    bug tests 
    opened by simonw 9
  • Use ?sql=xxx:signature instead of signed values

    Use ?sql=xxx:signature instead of signed values

    URLs to SQL queries currently look like this:

    https://simonwillison.net/dashboard/?sql=InNlbGVjdCAlKG5hbWUpcyBhcyBuYW1lLCB0b19jaGFyKGRhdGVfdHJ1bmMoJ21vbnRoJywgY3JlYXRlZCksICdZWVlZLU1NJykgYXMgYmFyX2xhYmVsLFxyXG5jb3VudCgqKSBhcyBiYXJfcXVhbnRpdHkgZnJvbSBibG9nX2VudHJ5IGdyb3VwIGJ5IGJhcl9sYWJlbCBvcmRlciBieSBjb3VudCgqKSBkZXNjIg%3A1lLfRD%3AvFP_m0s3BxRS2qyiWtlMlE1KRa2qoKItofP1vvK7hdY&sql=InNlbGVjdCBib2R5IGFzIGh0bWwgZnJvbSBibG9nX2VudHJ5IGxpbWl0IDEi%3A1lLfRD%3AEK0KOXcGgYdgD4Yzglbodf806GnbmrdtPridp8m0hlY

    The problem here is that if the Django secret is reset these become broken links - there's no easy way to recover the SQL.

    Instead, if the signatures do not match, how about populating the forms but NOT executing the SQL queries, and showing a warning message at the top of the page?

    The ?sql= parameters could then become ?sql=SELECT ...::oKItofP1vvK7hdY where oKItofP1vvK7hdY is a signature but the rest of the query is in plain text.

    enhancement security small 
    opened by simonw 8
  • Error: Object of type datetime is not JSON serializable

    Error: Object of type datetime is not JSON serializable

    Hello!

    This project is great. I've run into an issue though. I was trying to run this (unfinished) query:

    select m.id, m.pub_name, m.pub_id, m.contents_restricted, 
    	array_agg(s.updated_at) as sub_dates
    from main_manuscript m
    left join main_submission s on m.id=s.manuscript_id
    group by m.id, m.pub_name, m.pub_id, m.contents_restricted
    order by m.id
    

    The problem is that django-sql-dashboard errors out because it doesn't know how to serialize the s.updated_at DateTime field for working with array_agg.

    As far as I could tell the only way to fix this would be to fork the codebase? Am I missing something? Thanks!

    bug 
    opened by matthew-a-dunlap 6
  • Handle SQL queries that are too large for a GET request

    Handle SQL queries that are too large for a GET request

    Sometimes it's useful to run GIANT queries - queries with a huge copy-pasted list of IDs in them for example.

    Right now the POST works but the GET redirect may cause an error.

    If this happens, you can instead create a saved dashboard and the query will execute fine. Some kind of utility mechanism for spotting this and automatically handling it might be nice.

    enhancement 
    opened by simonw 6
  • Turn [count] into a cog action menu item, add more

    Turn [count] into a cog action menu item, add more

    Borrow the cog action menu design from Datasette. Actions can include:

    • Count values (the existing "count" link)
    • Sum / Avg / etc (for columns that are numbers)
    • Show not-null values
    enhancement 
    opened by simonw 6
  • Interface for creating and editing dashboards

    Interface for creating and editing dashboards

    This is currently only possible in the Django Admin. This will also implement edit permissions, taking over from #27.

    Still todo:

    • [x] Form to save a dashboard as a saved dashboard
    • [x] If dashboard reloads with form errors, scroll down to the form
    • [x] "Edit dashboard" link that links to the Django admin
    • [x] Custom Django admin code to respect edit policies
    • [x] Saved dashboard pages should show their visibility and edit policies
    enhancement 
    opened by simonw 6
  • Support markdown and/or HTML in dashboard descriptions

    Support markdown and/or HTML in dashboard descriptions

    In particular, I'm linking out to external sites in my dashboard descriptions, so it would be great to have those hyperlinked (currently the viewer has to manually copy/paste the URL, which is shown as plain text). Since the package already has both markdown and bleach as dependencies, it doesn't seem like this should be technically complex, although perhaps making it backwards-compatible might be non-trivial...

    Anyways, happy to discuss this further and start a PR if it's deemed a good idea.

    enhancement 
    opened by toolness 5
  • Error during

    Error during "docker-compose up"

    Hi, thank you for this project!

    While starting to run the dashboard interactively using the command docker-compose up I got an error Step 3/11 : COPY setup.py README.md . When using COPY with more than one source file, the destination must be a directory and end with a / The problem was fixed by adding a slash after the final dot in the Dockerfile, just as the error message tells, like so: COPY setup.py README.md ./

    I'm running Ubuntu 22.04.1 LTS. Docker version 20.10.18, build b40c2f6 docker-compose version 1.29.2, build unknown

    opened by reflektoin 0
  • Ability to customize individual cell rendering within tables - like widgets but at the table cell level

    Ability to customize individual cell rendering within tables - like widgets but at the table cell level

    Not sure if this pushes things a bit too far, but I thought it would be nice to be able to have a table on one dashboard, where each row displays a link that would take the user to a different dashboard that displays details about that row (e.g. by passing the id as a parameter for the other dashboard). This could be done e.g. by allowing to render individual cells as markdown or html. Essentially s.th. like this screenshot Not sure how difficult that would be though.

    enhancement 
    opened by jimmybutton 6
  • Figure out a way to display a title for individual queries/charts

    Figure out a way to display a title for individual queries/charts

    Many thanks for this great package!

    When creating a dashboard with multiple queries and charts, I'd like to be able to give all or some of them (especially the charts) a title so others can understand what the graph is showing without having to look at the SQL query. So what I ended up doing is having additional queries in between charts such as select '## Users per day' as markdown. It kinda does the job, but it ends up looking a bit weird as the title is then quite far away from the chart itself.

    A possible solution could be having an optional title field on the DashboardQuery object. What do you think about that?

    enhancement 
    opened by jimmybutton 1
  • Error on initial install: 'relation

    Error on initial install: 'relation "django_sql_dashboard_dashboard" does not exist'

    I'm trying to explore the functionality of the project for exploring a Postgres database, however I'm encountering the following exception immediately after installation. Maybe I am missing a configuration step? Any help is appreciated.

    Internal Server Error: /dashboard/
    Traceback (most recent call last):
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
    psycopg2.errors.UndefinedTable: relation "django_sql_dashboard_dashboard" does not exist
    LINE 1: ...id", "auth_group"."name", T6."id", T6."name" FROM "django_sq...
                                                                 ^
    
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/exception.py", line 55, in inner
        response = get_response(request)
      File "/usr/local/lib/python3.8/dist-packages/django/core/handlers/base.py", line 197, in _get_response
        response = wrapped_callback(request, *callback_args, **callback_kwargs)
      File "/usr/local/lib/python3.8/dist-packages/django/contrib/auth/decorators.py", line 23, in _wrapped_view
        return view_func(request, *args, **kwargs)
      File "/usr/local/lib/python3.8/dist-packages/django_sql_dashboard/views.py", line 121, in dashboard_index
        return _dashboard_index(
      File "/usr/local/lib/python3.8/dist-packages/django_sql_dashboard/views.py", line 325, in _dashboard_index
        saved_dashboards = [
      File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 320, in __iter__
        self._fetch_all()
      File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 1507, in _fetch_all
        self._result_cache = list(self._iterable_class(self))
      File "/usr/local/lib/python3.8/dist-packages/django/db/models/query.py", line 57, in __iter__
        results = compiler.execute_sql(
      File "/usr/local/lib/python3.8/dist-packages/django/db/models/sql/compiler.py", line 1361, in execute_sql
        cursor.execute(sql, params)
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 103, in execute
        return super().execute(sql, params)
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 67, in execute
        return self._execute_with_wrappers(
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
        return executor(sql, params, many, context)
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
      File "/usr/local/lib/python3.8/dist-packages/django/db/utils.py", line 91, in __exit__
        raise dj_exc_value.with_traceback(traceback) from exc_value
      File "/usr/local/lib/python3.8/dist-packages/django/db/backends/utils.py", line 89, in _execute
        return self.cursor.execute(sql, params)
    django.db.utils.ProgrammingError: relation "django_sql_dashboard_dashboard" does not exist
    LINE 1: ...id", "auth_group"."name", T6."id", T6."name" FROM "django_sq...
                                                                 ^
    
    question 
    opened by loganwilliams 1
  • Support non-string parameters and default values

    Support non-string parameters and default values

    Passing non-string parameters and default values can be achieved today using something like:

    select *
    where grade >= cast(%(grade)s as integer)
    and is_allowed = cast(coalesce(nullif(%(is_allowed)s,''), 'true') as boolean)
    

    But this is not very readable. Would be nice to support some other patterns, for example:

    select *
    where grade >= %(grade)d
    and is_allowed = %(is_allowed:true)b
    

    I know that psycopg2 supports only string parameters, but it does not seem too difficult to manually handle simple cases (like numbers and booleans) while still caring about SQL injections.

    For now, I've proposed pull request #148 to refactor the named-parameter feature within the library, so that developpers can easily extend it according to their needs. That would be usefull in any case.

    I think it would also be usefull that simple cases (like those mentionned above) be part of the library. What do you think?

    opened by ipamo 1
Releases(1.1)
  • 1.1(Apr 20, 2022)

  • 1.0.2(Mar 8, 2022)

    • Fixed a bug where queries that returned an array containing dates (or other objects without a defined Python JSON serialization) would result in a 500 error. #146
    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Jul 6, 2021)

  • 1.0(Jul 1, 2021)

    • Implemented a new column cog menu, with options for sorting, counting distinct items and counting by values. #57
    • Fixed bug where columns named after PostgreSQL reserved words (such as on or group) produced invalid suggested SELECT queries. #134
    • New Docker Compose configuration to support Docker development environments. Thanks, Atul Varma. #128
    • Admin change list view now only shows dashboards the user has permission to edit. Thanks, Atul Varma. #130
    Source code(tar.gz)
    Source code(zip)
  • 0.16(Jun 6, 2021)

    • This release includes a small potentially backwards-incompatible change: the description field for a saved dashboard is now treated as Markdown and rendered as such when the saved dashboard is displayed. It is very unlikely that this will affect any of your existing dashboards but you should still check before applying the upgrade. Thanks, Atul Varma. #115
    Source code(tar.gz)
    Source code(zip)
  • 0.15.1(Jun 3, 2021)

  • 0.15(May 25, 2021)

  • 0.14(May 16, 2021)

    • Fixed a security and permissions flaw, where users without the execute_sql permission could still run custom queries by editing saved dashboards using the Django admin interface. #94
    • Bar charts now preserve the order in which the data is returned by the query. #106
    • Example select statements now include explicit columns. #105
    • Columns on the dashboard page now respond to media queries, collapsing to a single column on narrow or mobile browser windows. #106
    • Fixed hard-coded /dashboard/ URL, thanks Flávio Juvenal da Silva Junior. #99
    • Fixed bug where ?_save- parameters could be accidentally reflected in the query string. #104
    • Explicitly require at least Django 3.0. #101
    • Fixed a warning about AutoField migrations with Django 3.2. #103
    • Fixed a bug where users lacking permissions could end up in an infinite redirect. #30
    • Configuration and security documentation now recommends using a read-only database replica as the most robust option. #95
    • Added screenshots and demo links for all of the included widgets. #96
    Source code(tar.gz)
    Source code(zip)
  • 0.13(May 10, 2021)

    • New word cloud widget displayed when queries return wordcount_word and wordcount_count columns. #91
    • All pages are now served with cache-control: private header if the user is logged in. #92
    • Much improved README, including a detailed list of features. #40
    Source code(tar.gz)
    Source code(zip)
  • 0.12.3(May 9, 2021)

    • Fixed bug where saved dashboards relating to groups could be displayed multiple times. #90
    • Removed duplicate "Run queries" button. #89
    • HTML page titles now include named parameter values, if available. #88
    Source code(tar.gz)
    Source code(zip)
  • 0.12.2(May 9, 2021)

    • Documentation is now hosted at https://django-sql-dashboard.datasette.io/ #86
    • Dashboard index link is no longer shown on saved dashboards to to users without permission to view it. #87
    Source code(tar.gz)
    Source code(zip)
  • 0.12.1(May 9, 2021)

  • 0.12(May 9, 2021)

    First non-alpha release! Django SQL Dashboard is now ready for people to use against their production Django applications.

    • Saved dashboards can now be created from the interactive dashboard page. #44
    • New progress bar widget for queries that return numeric columns total_count and completed_count. #77
    • The list of available tables now better reflects your current permissions, and shows the columns for each listed table. #79, #80
    • The dashboard index page now lists saved dashboards that the user is able to view or edit. #81
    • "Edit" link for dashboards links to the Django Admin, which now respects the edit policy set for a dashboard. #44
    • New documentation section covering security. #6
    • Show row count on non-truncated results. #76
    • More robust extraction of named parameteters from queries, fixing some errer-cases. #75
    • Custom widgets can now extend a django_sql_dashboard/widgets/_base_widget.html base template. #78
    • Fixed a bug caused by errors on saved dashboard pages being displayed as editable text. #74
    • Unlisted public dashboards now include a robots tag to avoid being indexed by search engines. #42
    Source code(tar.gz)
    Source code(zip)
  • 0.11a0(Apr 26, 2021)

  • 0.9a1(Apr 25, 2021)

  • 0.9a0(Apr 25, 2021)

  • 0.10a1(Apr 25, 2021)

  • 0.10a0(Apr 25, 2021)

  • 0.8a2(Apr 14, 2021)

  • 0.8a1(Apr 14, 2021)

  • 0.8a0(Apr 14, 2021)

    • Make it easy to provide a custom base template. #7
    • Content-Security-Policy: frame-ancestors header. #64
    • Signing no longer uses base64/json. #45
    • DASHBOARD_UPGRADE_OLD_BASE64_LINKS mechanism. #65
    Source code(tar.gz)
    Source code(zip)
  • 0.7a0(Apr 12, 2021)

  • 0.6a0(Apr 9, 2021)

  • 0.5a0(Mar 24, 2021)

  • 0.4a2(Mar 21, 2021)

  • 0.4a1(Mar 21, 2021)

  • 0.4a0(Mar 19, 2021)

    • Documentation now lives at https://django-sql-dashboard.readthedocs.io/ (#36)
    • Ability to copy and paste TSV from the default table display. (#29)
    • Fixed two bugs with the way count links in column headers work. (#31, #32)
    • New permissions system: a saved dashboard can now be made public, private, unlisted, group-only, staff-only or superuser-only. (#27)
    Source code(tar.gz)
    Source code(zip)
  • 0.3a1(Mar 16, 2021)

    • Changed default permission policy: saved dashboards are now inaccessible to the public by default (#37). This will change when permissions are implemented fully in #27.
    Source code(tar.gz)
    Source code(zip)
  • 0.3a0(Mar 15, 2021)

  • 0.2a2(Mar 15, 2021)

Owner
Simon Willison
Simon Willison
A simple porfolio with Django, Bootstrap and Sqlite3

Django Portofolio Example this is a basic portfolio in dark mode Installation git clone https://github.com/FaztWeb/django-portfolio-simple.git cd djan

Fazt Web 16 Sep 26, 2022
Create a netflix-like service using Django, React.js, & More.

Create a netflix-like service using Django. Learn advanced Django techniques to achieve amazing results like never before.

Coding For Entrepreneurs 67 Dec 08, 2022
Source code for Django for Beginners 3.2

The official source code for https://djangoforbeginners.com/. Available as an ebook or in Paperback. If you have the 3.1 version, please refer to this

William Vincent 10 Jan 03, 2023
REST API with Django and SQLite3

REST API with Django and SQLite3

Luis Quiñones Requelme 1 Nov 07, 2021
A django model and form field for normalised phone numbers using python-phonenumbers

django-phonenumber-field A Django library which interfaces with python-phonenumbers to validate, pretty print and convert phone numbers. python-phonen

Stefan Foulis 1.3k Dec 31, 2022
Probably the best abstract model / admin for your tree based stuff.

django-treenode Probably the best abstract model / admin for your tree based stuff. Features Fast - get ancestors, children, descendants, parent, root

Fabio Caccamo 360 Jan 05, 2023
Use watchfiles in Django’s autoreloader.

django-watchfiles Use watchfiles in Django’s autoreloader. Requirements Python 3.7 to 3.10 supported. Django 2.2 to 4.0 supported. Installation Instal

Adam Johnson 43 Dec 14, 2022
Official clone of the Subversion repository.

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. All documentation is in the "docs" directo

Raymond Penners 3 May 06, 2022
Django Login Api With Python

How to run this project Download and extract this project Create an environment and install all the libraries from requiements.txt pip freeze -r requi

Vikash Kisku 1 Dec 10, 2021
The little ASGI framework that shines. 🌟

✨ The little ASGI framework that shines. ✨ Documentation: https://www.starlette.io/ Community: https://discuss.encode.io/c/starlette Starlette Starlet

Encode 7.7k Dec 31, 2022
A Django web application to receive, virus check and validate transfers of digital archival records, and allow archivists to appraise and accession those records.

Aurora Aurora is a Django web application that can receive, virus check and validate transfers of digital archival records, and allows archivists to a

Rockefeller Archive Center 20 Aug 30, 2022
Add Chart.js visualizations to your Django admin using a mixin class

django-admincharts Add Chart.js visualizations to your Django admin using a mixin class. Example from django.contrib import admin from .models import

Dropseed 22 Nov 22, 2022
An insecure login and registration website with Django.

An insecure login and registration website with Django.

Luis Quiñones Requelme 1 Dec 05, 2021
this is a simple backend for instagram with python and django

simple_instagram_backend this is a simple backend for instagram with python and django it has simple realations and api in 4 diffrent apps: 1-users: a

2 Oct 20, 2021
PEP-484 stubs for Django

pep484 stubs for Django This package contains type stubs and a custom mypy plugin to provide more precise static types and type inference for Django f

TypedDjango 1.1k Dec 30, 2022
Easily share data across your company via SQL queries. From Grove Collab.

SQL Explorer SQL Explorer aims to make the flow of data between people fast, simple, and confusion-free. It is a Django-based application that you can

Grove Collaborative 2.1k Dec 30, 2022
Strawberry-django-plus - Enhanced Strawberry GraphQL integration with Django

strawberry-django-plus Enhanced Strawberry integration with Django. Built on top

BLB Ventures 138 Dec 28, 2022
Django Federated Login provides an authentication bridge between Django projects and OpenID-enabled identity providers.

Django Federated Login Django Federated Login provides an authentication bridge between Django projects and OpenID-enabled identity providers. The bri

Bouke Haarsma 18 Dec 29, 2020
Blog focused on skills enhancement and knowledge sharing. Tech Stack's: Vue.js, Django and Django-Ninja

Blog focused on skills enhancement and knowledge sharing. Tech Stack's: Vue.js, Django and Django-Ninja

Wanderson Fontes 2 Sep 21, 2022
English dictionary using Django based on freecodecamp

English Dictionary Hi there, i made this english dictionary using Django based on freecodecamp.org tutorial :) Table of Contents Preview Technologies

Aline Alencar 3 May 09, 2022