Django 및 React를 사용한 Python 채팅 자습서

59860 단어 djangochatreactpython
이 튜토리얼에서는 Python, Django 및 React로 채팅 애플리케이션을 빌드하는 방법을 설명합니다.

다른 튜토리얼과 달리 저는 WebSocket 연결에 Python/Django를 사용하지 않습니다. 그렇게 하는 것이 기술적인 관점에서 멋있어 보일 수 있지만, 특히 사용자 수가 절반 정도인 경우에는 매우 느리고 비용이 많이 듭니다. C++, Go 및 Elixir와 같은 언어는 채팅의 핵심을 처리하는 데 훨씬 뛰어납니다.

이 자습서에서는 Go, Raft 및 RocksDB를 사용하여 WebSocket 연결 및 기타 무거운 작업을 처리하는 Stream을 사용합니다.

목차:
  • React Chat Demo UI
  • Django/Python Setup
  • User Auth
  • Django Rest Framework
  • Generating Tokens to Access Stream's Chat Server
  • Integrating Auth in React
  • Sending a Message from the Python Server
  • Final Thoughts

  • The GitHub repo for the code below can be found at https://github.com/GetStream/python-chat-example.



    코딩하자! 🤓

    1단계 – 반응 채팅 데모 UI

    Before we start thinking about the Python chat side of things let's spin up a simple React frontend, so we have something nice and visual to look at:

    $ yarn global add create-react-app
    $ brew install node && brew install yarn # skip if installed
    $ create-react-app chat-frontend
    $ cd chat-frontend
    $ yarn add stream-chat-react
    

    Replace the code in src/App.js with:

    import React from "react";
    import {
      Chat,
      Channel,
      ChannelHeader,
      Thread,
      Window
    } from "stream-chat-react";
    import { MessageList, MessageInput } from "stream-chat-react";
    import { StreamChat } from "stream-chat";
    
    import "stream-chat-react/dist/css/index.css";
    
    const chatClient = new StreamChat("qk4nn7rpcn75"); // Demo Stream Key
    const userToken =
      "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1za3ktOSJ9.mhikC6HPqPKoCP4aHHfuH9dFgPQ2Fth5QoRAfolJjC4"; // Demo Stream Token
    
    chatClient.setUser(
      {
        id: "cool-sky-9",
        name: "Cool sky",
        image: "https://getstream.io/random_svg/?id=cool-sky-9&name=Cool+sky"
      },
      userToken
    );
    
    const channel = chatClient.channel("messaging", "godevs", {
      // image and name are required, however, you can add custom fields
      image:
        "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png",
      name: "Talk about Go"
    });
    
    const App = () => (
      <Chat client={chatClient} theme={"messaging light"}>
        <Channel channel={channel}>
          <Window>
            <ChannelHeader />
            <MessageList />
            <MessageInput />
          </Window>
          <Thread />
        </Channel>
      </Chat>
    );
    
    export default App;
    

    Next, run yarn start to see the chat in action!

    2단계 - Django/Python 설정(이미 있는 경우 건너뛰기)

    Make sure you have Python 3.7 up and running.

    $ brew install python3
    
    $ pip install virtualenv virtualenvwrapper
    $ export WORKON_HOME=~/Envs
    $ source /usr/local/bin/virtualenvwrapper.sh
    $ mkvirtualenv chatexample -p `which python3`
    $ workon chatexample
    

    If that does not work, please try this snippet:

    $ python3 -m venv chatexample
    $ source chatexample/bin/activate
    

    Now that you’re in your virtual env you should see python 3 when you run:

    $ python --version
    

    To kick off a new Django project, use the following snippet:

    $ pip install django
    $ django-admin startproject mychat
    

    And to start your app:

    $ cd mychat
    $ python manage.py runserver
    

    Now, when you open http://localhost:8000 , you should see this:



    3단계 - 사용자 인증

    As a next step lets setup Django’s user auth.

    $ python manage.py migrate
    $ python manage.py createsuperuser
    $ python manage.py runserver
    

    Visit http://localhost:8000/admin/ and login. Voila!

    You should see the Django admin screen as shown below:



    4단계 - Django Rest 프레임워크

    One of my favorite packages for integrating react with Django is Django Rest Framework. To make everything work, we will need to create endpoints for:

    • User Signup
    • User Login
    We could build those ourselves; however, there is a package called Djoser 이미 이 문제를 해결했습니다. 사용자 등록, 로그인, 비밀번호 재설정 등에 필요한 API 엔드포인트를 구성했습니다.

    Djoser를 설치하려면 다음 스니펫을 사용하십시오.

    $ pip install djangorestframework djoser
    


    그런 다음 편집urls.py하고 다음을 포함하도록 파일을 변경합니다.

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('auth/', include('djoser.urls')),
        path('auth/', include('djoser.urls.authtoken')),
    ]
    


    완료되면settings.py 편집하고 다음과 같이 변경합니다.

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'rest_framework.authtoken',
        'djoser',
    ]
    
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.TokenAuthentication',
        )
    }
    


    Djoser가 노출하는 API 엔드포인트에 대한 자세한 내용은 다음을 참조하세요.
    https://djoser.readthedocs.io/en/latest/sample_usage.html

    이제 등록 끝점을 테스트해 보겠습니다.

    $ curl -X POST http://127.0.0.1:8000/auth/users/ --data 'username=djoser&password=alpine12'
    


    5단계 - Stream의 채팅 서버에 액세스하기 위한 토큰 생성

    Now we need to customize the Djoser views to generate tokens for Stream. Let’s get started.

    Let's organize our files a bit and create a chat app folder in our project (make sure that you are in the correct directory):

    $ python manage.py startapp auth
    

    Install stream-chat:

    $ pip install stream-chat
    

    Create a custom serializer in auth/serializers.py with the following logic:

    from djoser.serializers import TokenSerializer
    from rest_framework import serializers
    from djoser.conf import settings as djoser_settings
    from stream_chat import StreamChat
    from django.conf import settings
    
    class StreamTokenSerializer(TokenSerializer):
        stream_token = serializers.SerializerMethodField()
    
        class Meta:
            model = djoser_settings.TOKEN_MODEL
            fields = ('auth_token','stream_token')
    
        def get_stream_token(self, obj):
            client = StreamChat(api_key=settings.STREAM_API_KEY, api_secret=settings.STREAM_API_SECRET)
            token = client.create_token(obj.user.id)
    
            return token
    

    And last, use the custom serializer by updating your settings.py file:

    STREAM_API_KEY = YOUR_STREAM_API_KEY # https://getstream.io/dashboard/
    STREAM_API_SECRET = YOUR_STREAM_API_SECRET
    DJOSER = {
        'SERIALIZERS': {
            'token': 'auth.serializers.StreamTokenSerializer',
        }
    }
    

    Rerun your migration:

    $ python manage.py migrate
    

    To verify that it works, hit the login endpoint with a POST request:

    $ curl -X POST http://127.0.0.1:8000/auth/token/login/ --data 'username=djoser&password=alpine12'
    

    Both the auth_token and stream_token should be returned.

    6단계 - React에 인증 통합

    Adding an auth later to the frontend is an essential step for obvious reasons. In our case, it’s especially useful because we can fetch a user token from the backend API (powered by Python) and dynamically use it when sending messages.

    First, install the CORS middleware package for Django:

    $ pip install django-cors-headers
    

    Then, modify your settings.py to reference the djors-cors-header middleware:

    INSTALLED_APPS = (
        ...
        'corsheaders',
        ...
    )
    
    MIDDLEWARE = [
        ...
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.common.CommonMiddleware',
        ...
    ]
    

    And finally, add the following to your settings.py file:

    CORS_ORIGIN_ALLOW_ALL = True
    

    The next step requires a few modifications to be made to your frontend. To start, you will want to ensure that you have all of the dependencies installed via yarn:

    $ yarn add axios react-dom react-router-dom
    

    Next, create the following files within your src/ directory:

    • AuthedRoute.js
    • UnauthedRoute.js
    • withSession.js
    • Login.js
    • Chat.js

    App.js

    import React from "react";
    import { BrowserRouter as Router, Switch } from "react-router-dom";
    
    import Chat from "./Chat";
    import Login from "./Login";
    
    import UnauthedRoute from "./UnauthedRoute";
    import AuthedRoute from "./AuthedRoute";
    
    const App = () => (
      <Router>
        <Switch>
          <UnauthedRoute path="/auth/login" component={Login} />
          <AuthedRoute path="/" component={Chat} />
        </Switch>
      </Router>
    );
    
    export default App;
    

    AuthedRoute.js

    import React from "react";
    import { Redirect, Route } from "react-router-dom";
    
    const AuthedRoute = ({ component: Component, loading, ...rest }) => {
      const isAuthed = Boolean(localStorage.getItem("token"));
      return (
        <Route
          {...rest}
          render={props =>
            loading ? (
              <p>Loading...</p>
            ) : isAuthed ? (
              <Component history={props.history} {...rest} />
            ) : (
              <Redirect
                to={{
                  pathname: "/auth/login",
                  state: { next: props.location }
                }}
              />
            )
          }
        />
      );
    };
    
    export default AuthedRoute;
    

    UnauthedRoute.js

    import React from "react";
    import { Redirect, Route } from "react-router-dom";
    
    const AuthedRoute = ({ component: Component, loading, ...rest }) => {
      const isAuthed = Boolean(localStorage.getItem("token"));
      return (
        <Route
          {...rest}
          render={props =>
            loading ? (
              <p>Loading...</p>
            ) : !isAuthed ? (
              <Component history={props.history} {...rest} />
            ) : (
              <Redirect
                to={{
                  pathname: "/"
                }}
              />
            )
          }
        />
      );
    };
    
    export default AuthedRoute;
    

    withSession.js

    import React from "react";
    import { withRouter } from "react-router";
    
    export default (Component, unAuthed = false) => {
      const WithSession = ({ user = {}, streamToken, ...props }) =>
        user.id || unAuthed ? (
          <Component
            userId={user.id}
            user={user}
            session={window.streamSession}
            {...props}
          />
        ) : (
          <Component {...props} />
        );
    
      return withRouter(WithSession);
    };
    

    Login.js

    import React, { Component } from "react";
    import axios from "axios";
    
    class Login extends Component {
      constructor(props) {
        super(props);
    
        this.state = {
          loading: false,
          email: "",
          password: ""
        };
    
        this.initStream = this.initStream.bind(this);
      }
    
      async initStream() {
        await this.setState({
          loading: true
        });
    
        const base = "http://localhost:8000";
    
        const formData = new FormData();
        formData.set("username", this.state.email);
        formData.set("password", this.state.password);
    
        const registration = await axios({
          method: "POST",
          url: `${base}/auth/users/`,
          data: formData,
          config: {
            headers: { "Content-Type": "multipart/form-data" }
          }
        });
    
        const authorization = await axios({
          method: "POST",
          url: `${base}/auth/token/login/`,
          data: formData,
          config: {
            headers: { "Content-Type": "multipart/form-data" }
          }
        });
    
        localStorage.setItem("token", authorization.data.stream_token);
    
        await this.setState({
          loading: false
        });
    
        this.props.history.push("/");
      }
    
      handleChange = e => {
        this.setState({
          [e.target.name]: e.target.value
        });
      };
    
      render() {
        return (
          <div className="login-root">
            <div className="login-card">
              <h4>Login</h4>
              <input
                type="text"
                placeholder="Email"
                name="email"
                onChange={e => this.handleChange(e)}
              />
              <input
                type="password"
                placeholder="Password"
                name="password"
                onChange={e => this.handleChange(e)}
              />
              <button onClick={this.initStream}>Submit</button>
            </div>
          </div>
        );
      }
    }
    
    export default Login;
    

    Chat.js

    import React, { Component } from "react";
    import {
      Chat,
      Channel,
      ChannelHeader,
      Thread,
      Window
    } from "stream-chat-react";
    import { MessageList, MessageInput } from "stream-chat-react";
    import { StreamChat } from "stream-chat";
    
    import "stream-chat-react/dist/css/index.css";
    
    class App extends Component {
      constructor(props) {
        super(props);
        this.client = new StreamChat("<YOUR_STREAM_APP_ID>");
    
        this.client.setUser(
          {
            id: "cool-sky-9",
            name: "Cool Sky",
            image: "https://getstream.io/random_svg/?id=cool-sky-9&name=Cool+sky"
          },
          localStorage.getItem("token")
        );
    
        this.channel = this.client.channel("messaging", "godevs", {
          image:
            "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png",
          name: "Talk about Go"
        });
      }
    
      render() {
        return (
          <Chat client={this.client} theme={"messaging light"}>
            <Channel channel={this.channel}>
              <Window>
                <ChannelHeader />
                <MessageList />
                <MessageInput />
              </Window>
              <Thread />
            </Channel>
          </Chat>
        );
      }
    }
    
    export default App;
    

    Be sure to replace YOUR_STREAM_APP_ID with a valid Stream App ID which can be found on the .

    Restart your frontend application and you should be hit with an auth wall! Enter your email and password and a token will be requested and stored in local storage.

    7단계 - Python 채팅 서버에서 메시지 보내기

    Occasionally, you will want to write to the chat API using your backend Python-based server. Here’s a quick management command that you can use:

    Verify that installed apps looks like this in settings.py :

    INSTALLED_APPS = [
        'corsheaders',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'rest_framework.authtoken',
        'djoser',
    ]
    

    Next, create the directory chat/management/commands . In that directory, add a file called broadcast.py with this content:

    from django.core.management.base import BaseCommand, CommandError
    from django.conf import settings
    from stream_chat import StreamChat
    
    class Command(BaseCommand):
        help = 'Broadcast the message on your channel'
    
        def add_arguments(self, parser):
            parser.add_argument('--message')
    
        def handle(self, *args, **options):
            client = StreamChat(api_key=settings.STREAM_API_KEY, api_secret=settings.STREAM_API_SECRET)
            client.update_user({"id": "system", "name": "The Server"})
            channel = client.channel("messaging", "kung-fu")
            channel.create("system")
            response = channel.send_message({"text": "AMA about kung-fu"}, 'system')
            self.stdout.write(self.style.SUCCESS('Successfully posted a message with id "%s"' % response['message']['id']))
    

    You can try posting a message to the chat like this:

    $ python manage.py broadcast --message hello
    

    And you should see a response like this:



    마지막 생각들

    I hope you enjoyed this tutorial on building a chat application with Django, Python and React!

    For an interactive tour of , please have a look at our on the Stream website. If you are interested in digging into the code for Stream Chat React Components, the full docs can be found here . Stream 위에 채팅을 구축하는 데 관심이 있는 경우 최신 버전SDKs과 함께 인기 있는 언어 및 프레임워크에 대한 다양한iOS (Swift)을 제공합니다.

    즐거운 코딩! ✌

    좋은 웹페이지 즐겨찾기