#Struggling with making test changes to deployment (added database configurations)

194 messages Β· Page 1 of 1 (latest)

sonic shore
#

Hey friends, I have a django app deployed on a ubuntu VPS with Postgres, Nginx, and Gunicorn.

I need to make some updates to the app, specifically I'm aiming to set up user logins, so I want to be uploading inputted user data to my postgres DB, but I'm struggling to figure out how I can set this up for testing locally before making the changes in production.

I have already got the django app in a git repo and the idea of making changes locally in vsCode to the styling CSS or whatever and then going back to the VPS to pull the changes is a workflow that makes sense to me. But I'm struggling with how I can access a copy of the VPS's postgres DB locally to play around with, if that makes sense?

Happy to answer clarifying questions - I'm super new to this so apologies if I don't make much sense

What I've tried:
I've tried allowing my local IP to connect to the VPS datbase by editing the postgresql.conf file and pg_hba.conf files in the VPS, but I get connection refused errors and I'm not sure why at this stage - I'm thinking maybe it would be easier if I just had a local "copy" of the DB instead to test with

raw fiber
#

Installing a local copy is definitely the way to go. You don't really want to mess about with production data while running tests.
What OS are you developing on?

sonic shore
#

I'm on Windows

raw fiber
#

Damn πŸ˜…

sonic shore
#

Does that make it very difficult?

#

or is it just that you're not on Windows

raw fiber
#

It does for me as I haven't properly used it in over a decade 😁

sonic shore
#

I see 😦

raw fiber
#

I generally recommend using env vars for configuration, as I think they are the cleanest solution. You can set them on a per-unit basis assuming you deployed gunicorn via systemd.

sonic shore
#

i have installed postgres locally although it is a different version, does this matter?

raw fiber
#

Assuming they aren't decades apart, probably not

sonic shore
#

assuming you deployed gunicorn via systemd.
I'm not sure what systemd is tbh

raw fiber
#

You wrote a .service file?

sonic shore
#

yes I did

raw fiber
#

Yeah, then you're using systemd πŸ™‚

#

systemctl is a systemd management command.

sonic shore
#

I see, I see

raw fiber
sonic shore
#

Okay cool

#

I will take a look at the resources you sent for now, I might ping you if I feel lost if that's okay

raw fiber
#

Of course πŸ™‚

#

If anything is unclear, do reach out πŸ™‚

sonic shore
#

I'm kind of wondering... Can it be as simple as me pulling the git repo locally and running python manage.py runserver ?

That would just mean that I'd need to make a local copy of the DB manually and then any changes to the DB I make locally, I would need to manually make them in the VPS DB too?

#

and for the rest, such as the login form/survey, I can just pull the code into the VPS using git?

#

I'm considering this because the DB structure only needs to be pretty basic

raw fiber
#

You should be using python manage.py migrate to make changes go the DB (assuming you mean the schema, not data).

sonic shore
#

Correct I meant schema

raw fiber
#

Yeah, then you would edit your models locally, run python manage.py makemigrations, push those, and run migrate on the server.
It's a good idea to have it as a ExecStartPre in the service file.

sonic shore
#

So I think where I'm lost is replicating the DB locally then...

I just made a clone of the VPS app files locally and then did manage.py runserver, but I get a long error ending with these lines

connection = self.Database.connect(**conn_params)
  File "C:\Users\Jamie\AppData\Local\Programs\Python\Python39\lib\site-packages\psycopg2\__init__.py", line 122, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
django.db.utils.OperationalError: connection to server at "localhost" (::1), port 5432 failed: FATAL:  password authentication failed for 
user "admin"

Which I'm guessing is because I don't have a local copy of the DB set up?

raw fiber
#

You need to configure permissions...

#

Did you create an admin user?

raw fiber
# raw fiber Yeah, then you would edit your models locally, run `python manage.py makemigrati...

Off the top of my head, so it's probably not going to work out of the box, but something like this:

[Unit]
Description=My Django app

[Service]
Environment="DJANGO_DB_URL=postgresql://myprojectuser:password@localhost/myproject"
ExecStartPre=/path/to/venv/bin/python manage.py migrate
ExecStartPre=/path/to/venv/bin/python manage.py collectstatic
ExecStart=/path/to/venv/bin/gunicorn [options]
sonic shore
#

I think so I ran a command like ~/myprojectdir/manage.py createsuperuser

#

on the VPS

raw fiber
#

createsuperuser creates a Django administrator. It's not the same thing.

#

You need to create a postgresql user for the database connection.

sonic shore
#

ah yes, it was this command CREATE USER myprojectuser WITH PASSWORD 'password';

#

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

#

and then I have the username and password in the django app settings.py file

#
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}
raw fiber
#

What Django version are you on? And how did you install psycopg(2?)

sonic shore
#

bare with, just trying to figure out how to check the version

#

how did you install psycopg(2?)
on the VPS, I created a virtual environment, activated it, and then ran pip install django gunicorn psycopg2-binary

#

I haven't installed it locally

raw fiber
#

Ok. PsycoPG2 is older, and likely to be deprecated soon. If your Django version is new-ish, it's a good idea to replace it with psycopg-binary (version 3).

sonic shore
#

django will be newish because I only set this up a couple weeks ago

raw fiber
#

Also, as I said, hardcoding secrets in the settings file (including DB credentials) is generally a bad idea.

sonic shore
#

okay, I'll add that to the todo list

raw fiber
#

Updated the service unit above with an env var to set the DB URL.

If you use e.g. cbs, you can use something like this:

# settings.py
import os
from cbs import BaseSettings, env

DB_URL = os.environ.get("DJANGO_DB_URL", "postresql://devuser:insecurepassword@localhost/myproject")

class Settings(BaseSettings):
    def DATABASES(self):
        return {
            "default": env.dburl(DB_URL),
        }

Or check the docs for dj-database-url if you'd rather use that.
This would allow you to use devuser and insecurepassword as the local credentials, and set them securely in the service file.

willow vessel
#

If you want to play with a copy of the production database, you can, (up to a number of rows where it is sloooow) use dumpdata and loaddata to copy all data fra prod to dev db πŸ€”

willow vessel
#

If you have media files in the DB you will also have to copy med media folder and have the same relative paths

willow vessel
sonic shore
#

Tbh I feel like I’ve deviated this a lot from what I’m trying to achieve, which I feel should be a pretty painless process

For now, I just want to get a copy of my VPS app working locally so I can start adding new features (e.g. login system)

I guess I’m just wondering what the easiest way to do this is?

#

Or is it just not that simple?

raw fiber
#

While it may sometimes lead to bugs, you can always use sqlite in dev, and postgres in production.

#

The easiest way to replicate an identical setup is using Docker, tbh (but that's only easy if you spend days getting comfortable with docker).

raw fiber
willow vessel
#

Since both my prod and dev is using sqlite, i can just copy the database and media files directly.

sonic shore
raw fiber
#

Yeah, then the first step would be to create that user locally so Django can use the postgres DB

willow vessel
#

(or just use sqlite for local dev)

sonic shore
#

Whichever is easiest πŸ˜…

#

Or better - whichever im least likely to mess up

raw fiber
#

Well, using sqlite for local dev would require at least setting the DB conditionally.

#

Which would also require setting DEBUG conditionally πŸ€”

#

Or using different env vars.

#

I think I'd go with setting up the postgres user

sonic shore
#

Agreed!

#

And I only need the user, not to create a local database with the same name too?

raw fiber
#

Not sure... it probably doesn't hurt to create the DB πŸ€”

#

I usually just use docker, so it's created automagically.... been a hot minute since I've used postgres on bare metal.

sonic shore
#

Think I have progress....

So I set up a local DB and user

Server [localhost]:
Database [postgres]:
Port [5432]:
Username [postgres]:
Password for user postgres:
psql (17.2)
WARNING: Console code page (850) differs from Windows code page (1252)
         8-bit characters might not work correctly. See psql reference
         page "Notes for Windows users" for details.
Type "help" for help.

postgres=# CREATE DATABASE main;
CREATE DATABASE
postgres=# CREATE USER admin WITH PASSWORD 'x';
CREATE ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE main TO admin;
GRANT
postgres=#
#

Then I ran python manage.py migrate

this gave me a big error ending

 raise MigrationSchemaMissing(
django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table (permission denied for schema publicLINE 1: CREATE TABLE "django_migrations" ("id" bigint NOT NULL PRIMA...
                     ^
)
#

BUT, if I run python manage.py runserver now, the app starts! Although it just says Bad Request (400)

raw fiber
#

Is the name main what you have in settings?

#

Or is the name public? πŸ€”

sonic shore
#

that is what I have in settings

#
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'main',
    'USER': 'admin',
    'PASSWORD': 'x',
    'HOST': 'localhost',
    'PORT': '',
    }
}
raw fiber
#

Try this SQL:

GRANT ALL PRIVILEGES ON SCHEMA public TO admin; 

πŸ€”

sonic shore
#
raise MigrationSchemaMissing(
django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table (permission denied for schema public
LINE 1: CREATE TABLE "django_migrations" ("id" bigint NOT NULL PRIMA...

same error :C

#

when running migrate

#
postgres=# GRANT ALL PRIVILEGES ON SCHEMA public TO admin;
GRANT
raw fiber
#

Check python manage.py diffsettings for the line starting with DATABASES πŸ€”

#

It should show the correct settings, hopefully

sonic shore
#

It did show a bunch of settings, was there anything you wanted in particular?

#

or just to check it works

raw fiber
#

DATABASES

sonic shore
#

DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'main', 'USER': 'admin', 'PASSWORD': 'x', 'HOST': 'localhost', 'PORT': '', 'ATOMIC_REQUESTS': False, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'CONN_HEALTH_CHECKS': False, 'OPTIONS': {}, 'TIME_ZONE': None, 'TEST': {'CHARSET': None, 'COLLATION': None, 'MIGRATE': True, 'MIRROR': None, 'NAME': None}}}

raw fiber
sonic shore
#

looking into....

#

So I ran the following commands in psql according to the thread:

-- Connect to mydb
\connect main;
-- Create a new schema with myuser as owner
CREATE SCHEMA myschema AUTHORIZATION admin;

ALTER ROLE admin SET client_encoding TO 'utf8';
ALTER ROLE admin SET default_transaction_isolation TO 'read committed';
ALTER ROLE admin SET timezone TO 'UTC';
#

then I re-ran ./manage.py migrate - but am getting the same error for now πŸ₯Ή

raw fiber
#

Did you update the settings with the options key?

sonic shore
#

oooo I did not, thank u

#

oooomg

#

getting all the nice green OK's now !

raw fiber
#

So much for "easiest and quickest" but we got there πŸ˜‚

sonic shore
#

I re-ran runserver but it still just says bad request though :\ was hoping that would fix it

raw fiber
#

Enable DEBUG?

#

It's probably something like ALLOWED_HOSTS πŸ€”

sonic shore
#

Yes, that's right!

#

DisallowedHost at /
Invalid HTTP_HOST header: '127.0.x.x:8000'. You may need to add '127.0.x.x' to ALLOWED_HOSTS.

#

so just a settings tweak hopefully, lemme try

#

okaaaaay πŸ™‚ working

#

albeit none of the static files are serving

#

WOOOOOOOOOOOOOOOOOOOO

raw fiber
#

Django doesn't serve static files if DEBUG is False πŸ˜…

sonic shore
#

really? It has worked for me previously

#

with CSS and images

#

Oh wasn't there a command

#

python manage.py collectstatic

raw fiber
#

It really shouldn't, but if you run runserver, you can set DEBUG=True and simply not commit that πŸ€”

sonic shore
#

Yeah but don't get me wrong, even with DEBUG=False, the static files aren't servinng

#

so far I've tried python manage.py collectstaticbut that didn't resolve

#

but it confirmed that it can see the static folder atleast

raw fiber
#

You are running runserver, and not gunicorn or something? πŸ€”

sonic shore
#

oh yeah, I am just running manage.py runserver - is that not right? πŸ˜…

#

i thought that would be fine for local development

raw fiber
#

Yeah, that's fine

#

And you see 404s listed in the console for static files, when you load the page?

sonic shore
#

Yes correct, e.g:

[18/Dec/2024 15:42:42] "GET /static/styles/style.css HTTP/1.1" 404 179
[18/Dec/2024 15:42:42] "GET /static/images/home_icon.png HTTP/1.1" 404 179
[18/Dec/2024 15:42:42] "GET /static/images/colour_semantics_icon.png HTTP/1.1" 404 179
[18/Dec/2024 15:42:42] "GET /static/images/base_icon.png HTTP/1.1" 404 179```
raw fiber
#

python manage.py findstatic styles/style.css?

sonic shore
#

File path is certainly looking correct

sonic shore
raw fiber
#

What do the static file settings look like?

sonic shore
#
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')```
#

thats all

#

forgot a line

raw fiber
#

No STATICFILES_DIRS?

sonic shore
#

yes static_root, as above

#

no staticfiles_dirs

#

it works on the VPS tho πŸ€”

raw fiber
#

You serve them via nginx?

sonic shore
#

I do

#
    os.path.join(BASE_DIR, 'static'),
)```
#

will try adding this....

sonic shore
#

ah ok

raw fiber
#

Can't have STATIC_ROOT in there πŸ˜…

#

What do the nginx settings look like for static?

sonic shore
#

uhmm - I think I'm a bit silly

#

I've got the django app proj files cloned locally, but that doesn't include any nginx files/settings I think?

raw fiber
#

Yeah, that's fine...

sonic shore
#

oh - so u wanna see the settings from the VPS?

raw fiber
#

More curious about how it looks in production

sonic shore
#

okay okay

#
server {
    server_name little-talk.org www.little-talk.org;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        alias /home/admin/littleTalk/static/;
    }

...
raw fiber
#

Yeah, you should have static input directories, and a static output directory...

#

I guess we can worry about best practices later πŸ˜…

#

And skip committing another change πŸ€”

raw fiber
#
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

Add this, but delete the STATIC_ROOT setting πŸ€”

#

Then don't commit settings.py to git πŸ˜…

#

(That's a good reason to use CBS, and have different settings for different environments :p)

sonic shore
#
 python manage.py findstatic styles/style.css
Found 'styles/style.css' here:
  C:\Users\Jamie\Documents\GitHub\littleTalk-VPS\static\styles\style.css``` 
😏
#

oh interesting... still doesn't work

"GET /static/images/speech_therapy.png HTTP/1.1" 404 179
[18/Dec/2024 16:05:46] "GET /static/images/colour_semantics_icon.png HTTP/1.1" 404 179
[18/Dec/2024 16:05:46] "GET /static/images/stock_graphs.jpg HTTP/1.1" 404 179
[18/Dec/2024 16:05:46] "GET /static/images/learning_level.png HTTP/1.1" 404 179
[18/Dec/2024 16:05:46] "GET /favicon.ico HTTP/1.1" 404 179
raw fiber
#

Try findstatic on one of those? πŸ€”

#

(Ignore favicon.ico)

sonic shore
#

findstatic works ok

raw fiber
#

(β•―Β°β–‘Β°)β•―οΈ΅ ┻━┻

sonic shore
#

it's gonna be okay β”¬β”€β”¬γƒŽ( ΒΊ _ ΒΊγƒŽ)

raw fiber
#

DEBUG still True?

sonic shore
#

nopee

raw fiber
#

Oh, that be why :p

#

Django only serves static when DEBUG is True

sonic shore
#

ehh??

#

Oh THATS what u meant

#

i thought u meant the other way round

#

AWWWWWW SHIT

#

we're up and running 🎊

#

I can't thank you enough Pat πŸ˜…

#

Days of me going crazy solved in a matter of hours

#

My steplist when deploying to prod:

`Local:
python manage.py makemigrations (if there is local DB changes)
Commit local changes (NOT SETTINGS.PY)

VPS:
Git pull
python manage.py migrate (if I have migrations)
python manage.py collectstatic (if I have new statics)`

raw fiber
#

Sounds right... and restart the gunicorn server, of course

#

Though we'll need to fix your static file settings one of these days πŸ˜…

sonic shore
#

I'll do it next

raw fiber
#

Which will also mean updating nginx settings, and comitting some changes to settings.py

sonic shore
#

Okay... I'm feeling confident that I can have a go myself now that the ball is rolling! πŸ™‚

#

random question, whenever I runserver etc, git wants to commit loads of changes on a binary level - is this normal?

#

I couldn't really find much online about it

raw fiber
#

See my previous suggestions of keeping static in /srv/http/static/ (or /srv/http/littletalk/static/ if you have multiple projects)
(In STATIC_ROOT and nginx.conf)

#

What files?

sonic shore
#

for example: littleTalkApp\__pycache__\urls.cpython-39.pyc

#

im using git desktop and it says "the binary file has changed"

raw fiber
#

Yeah, add **/*.pyc to .gitignore, and run git rm on any that are already committed.

#

Those are python bytecode files... they should not be committed πŸ™‚

sonic shore
#

okay, good thing I checked then πŸ˜›