Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-Firebase bugs - happy to help fix #173

Open
RobSisson opened this issue Nov 24, 2022 · 2 comments
Open

Non-Firebase bugs - happy to help fix #173

RobSisson opened this issue Nov 24, 2022 · 2 comments

Comments

@RobSisson
Copy link

Hi; love the project!

Just want to preface this with the fact I'm pretty new to Django, and basically everything I'm talking about here I've figured out from trial and error, so there is more than likely some significant errors in my logic. I also based my changes directly from the documentation, which in some cases may be overly simplistic or not as fit for purpose as what is written, however I'm unable to ascertain whether or not this is the case due to my inexperience. Therefore, please accept my apology for any ignorant suggestions, and I'd greatly appreciate being corrected.

That said, I've been using the web version a bit without using Firebase found a few different issues, as followed. I'm very aware that a lot of the issues with channels may be caused by me not using Firebase, however I'd prefer to arrange the backend myself, and thought others may also want to do the same.

1. Issues with Generic Relations
When creating a GenericForeignKey the model comes out as:

class Tags(models.Model):

    # Relationships
    content_type = models.ForeignKey("contenttypes.ContentType", on_delete=models.CASCADE, related_query_name='tags' )

    # Fields
    object_id = models.IntegerField()
    created = models.DateTimeField(auto_now_add=True, editable=False)
    slug = models.SlugField()
    tag = models.CharField(max_length=30)
    last_updated = models.DateTimeField(auto_now=True, editable=False)
    link = models.GenericForeignKey("content_type", "object_id")

There are two missing imports, and two uses which need adjusting, namely ContentType and GenericForeignKey are both recommended to be imported, then called, eg:

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Tags(models.Model):

    # Relationships
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_query_name='tags' )

    # Fields
    object_id = models.IntegerField()
    created = models.DateTimeField(auto_now_add=True, editable=False)
    slug = models.SlugField()
    tag = models.CharField(max_length=30)
    last_updated = models.DateTimeField(auto_now=True, editable=False)
    link = GenericForeignKey("content_type", "object_id")


2. Issues with creating user models
When creating a User model, AbstractUser is not imported in the models, this would require

from django.contrib.auth.models import AbstractUser

In addition, if one is creating their own user model using AbstractUser, then they need to name it as such in the settings file:

AUTH_USER_MODEL = "User_App_Name.User_Model_Name"

This would then enable py manage.py makemigrations to be ran, and the server launched.


3. Django Channels Issues

Following the server launch, HTMX works (thank you for adding this! An unreal addition!), however Django Channels doesn't work as expected, getting the following:

Not Found: /ws/ [24/Nov/2022 08:13:36] "GET /ws/ HTTP/1.1" 404 2532
image

After doing some digging, I found that the depreciated django.conf.urls.url() is used in the routing.py file, however this was removed in 4.0.

When exploring the Django Channels documentation to try to figure out this issue, I came across a few things:

Firstly, I found that the depreciated django.conf.urls.url() is used in the routing.py file, however this was removed in 4.0.

In an attempt to solve this, I toyed around with using re_path instead, however this doesn't function as hoped with the existing code.

Secondly, the asgi and routing files are quite different in structure to the recommended docs (though I'm aware the docs I'm referencing are very simplistic and its possible that things can be done differently - please explain the logic behind doing so if that's the case!)

asgi.py on download:

"""
ASGI entrypoint. Configures Django and then runs the application
defined in the ASGI_APPLICATION setting.
"""

import os
import django
from channels.routing import get_default_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Journey1.settings")
django.setup()
application = get_default_application()

doc recommended asgi.py

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
from django.urls import path

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

from chat.consumers import AdminChatConsumer, PublicChatConsumer

application = ProtocolTypeRouter({
    # Django's ASGI application to handle traditional HTTP requests
    "http": django_asgi_app,

    # WebSocket chat handler
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter([
                path("chat/admin/", AdminChatConsumer.as_asgi()),
                path("chat/", PublicChatConsumer.as_asgi()),
            ])
        )
    ),
})

routing.py on download

from django.conf.urls import url

from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack

from Journey1.consumers import Journey1_WebSocketConsumer

# Consumer Imports
from Events.consumers import EventsConsumer
from User.consumers import UserConsumer


application = ProtocolTypeRouter({

    # WebSocket handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^ws/$", Journey1_WebSocketConsumer.as_asgi()),
        ])
    ),
    "channel": ChannelNameRouter({
        "Events": EventsConsumer,    "User": UserConsumer,
    })
})

doc recommended routing - This documentation also discusses the use of re_path instead of url:

# chat/routing.py
from django.urls import re_path

from . import consumers

websocket_urlpatterns = [
    re_path(r"ws/chat/(?P<room_name>\w+)/$", consumers.ChatConsumer.as_asgi()),
]

From what I can see it seems that some of the functions, namely ProtocolTypeRouter is in the routing.py file, rather than the asgi.py as recommended. Other than the differing structure, there is the addition use of AllowedHostsOriginValidator in the docs.

I followed the recommended format seen in the documentation, adjusting imports from
from Journey1.consumers import Journey1_WebSocketConsumer (in routing.py) & from Journey1.routing import application (in asgi.py)
to
from .consumers import Journey1_WebSocketConsumer & from .routing import application

Setting up daphne

Including changing the settings.py to follow channels set up guidelines:

# Application definition INSTALLED_APPS = [ 'django.contrib.admin',...

to

# Application definition INSTALLED_APPS = [ "daphne", 'django.contrib.admin',

ASGI_APPLICATION = "Journey1.routing.application"
to
ASGI_APPLICATION = "Journey1.asgi.application"

Then the server & channels function as expected, without the /ws/ 404 bug.

Starting ASGI/Daphne version 4.0.0 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
HTTP GET / 200 [0.03, 127.0.0.1:52304]
WebSocket HANDSHAKING /ws/ [127.0.0.1:52310]
WebSocket CONNECT /ws/ [127.0.0.1:52310]
Received text=Text from Javascript
Received bytes=b'Bytes from Javascript'

image

(the closed port error is related to chrome extensions)


I've taken a look into the files, and think that I might be able to tackle some of the issues which are contained in the python files, however some of the population issues are a little more complex (for example the conditional imports of GenericForeignKey / ContentType / AbstractUser).

Curious to hear your thoughts on the above, and totally understand if you don't have interest in pulling the above as it goes against your choice of using firebase, though perhaps some of the issues may persist across on to firebase implementations as well.

Thanks for reading!

@mmcardle
Copy link
Owner

HI @RobSisson ,

Thanks so much for your feedback, what you are saying makes a lot of sense.
I will comment on some of the bugs/issues in separate comments later.

But on the general firebase comments I am working on separating the code into multiple libraries in a single monorepo which would make it easier to understand and to extend.
So far this would consist of the following

  • @djangobuilder/core - The core rendering library with minimal dependencies
  • @djangobuilder/ui - Some HTML component (possibly vue/react) (Not sure if this is required/needed)
  • @djangobuilder/cli - A command line tool for creating Django projects building on djangobuilder/core
  • @djangobuilder/io - The firebase backed project that is currently deployed at djangobuilder.io (builds on top of djangobuilder/core and djangobuilder/ui)

This would allow you to build you own libraries, projects or sites using the core rendering functionality.

I hope I have explained this well enough, only really started working on it this week.

M

@RobSisson
Copy link
Author

Hey @mmcardle, thanks for your reply,

Love the logic and totally understand the idea behind it, defo a nice idea to split things out into the presented libraries.

From my perspective, I'd appreciate the web ui being preserved as I find the visual interface really useful for quickly planning and editing, especially as I'm reasonably new to Django. Also the interface you already have developed works nicely, not much would need to be changed as far as I'm concerned, but perhaps it could just be included in the io package to reduce your time investment and since I personally at least wouldn't want to develop my own interface for it when yours is so suitable.

Will definitely keep tabs on the progress of this, and am more than happy to help with the bugs I highlighted. It's the first package I'll have contributed to, so I'll have to figure how to do it, but let me know if that'd be appreciated - feel free to ask me to help directly!

Also will be building a hotwire django site using a django builder with HTMX produced foundation, so I'll see once that's made if I've got time to contribute that alternative option, and can ofc discuss if you'd like to take it that way. The potential for it is super cool, effectively a ui/cli build out a strong basis for a django backend. Almost like cooking the cookie a cookiecutter project would make you - cookedcookie-django! hahaha

Looking forward to hearing your feedback on the points I raised, especially as I'm sure they'll teach me a lot.

Best,
R

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants