معرفی شرکت ها


django-sorcery-0.9.4


Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر
Card image cap
تبلیغات ما

مشتریان به طور فزاینده ای آنلاین هستند. تبلیغات می تواند به آنها کمک کند تا کسب و کار شما را پیدا کنند.

مشاهده بیشتر

توضیحات

Django and SQLAlchemy integration
ویژگی مقدار
سیستم عامل -
نام فایل django-sorcery-0.9.4
نام django-sorcery
نسخه کتابخانه 0.9.4
نگهدارنده []
ایمیل نگهدارنده []
نویسنده Serkan Hosca
ایمیل نویسنده serkan@hosca.com
آدرس صفحه اصلی https://github.com/shosca/django-sorcery
آدرس اینترنتی https://pypi.org/project/django-sorcery/
مجوز MIT
############################################################# Django Sorcery - Django Framework integration with SQLAlchemy ############################################################# |Build Status| |Read The Docs| |PyPI version| |Coveralls Status| |Black| * Free software: MIT license * GitHub: https://github.com/shosca/django-sorcery SQLAlchemy is an excellent orm. And Django is a great framework, until you decide not to use Django ORM. This library provides utilities, helpers and configurations to ease the pain of using SQLAlchemy with Django. It aims to provide a similar development experience to building a Django application with Django ORM, except with SQLAlchemy. Installation ============ :: pip install django-sorcery Quick Start =========== Lets start by creating a site: .. code:: console $ django-admin startproject mysite And lets create an app: .. code:: console $ cd mysite $ python manage.py startapp polls This will create a polls app with standard django app layout: .. code:: console $ tree . ├── manage.py ├── polls │   ├── admin.py │   ├── apps.py │   ├── __init__.py │   ├── migrations │   │   └── __init__.py │   ├── models.py │   ├── tests.py │   └── views.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py 3 directories, 12 files And lets add our ``polls`` app and ``django_sorcery`` in ``INSTALLED_APPS`` in ``mysite/settings.py``: .. code:: python INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django_sorcery', 'polls.apps.PollsConfig', ] Now we're going to make a twist and start building our app with ``sqlalchemy``. Lets define our models in ``polls/models.py``: .. code:: python from django_sorcery.db import databases db = databases.get("default") class Question(db.Model): pk = db.Column(db.Integer(), autoincrement=True, primary_key=True) question_text = db.Column(db.String(length=200)) pub_date = db.Column(db.DateTime()) class Choice(db.Model): pk = db.Column(db.Integer(), autoincrement=True, primary_key=True) choice_text = db.Column(db.String(length=200)) votes = db.Column(db.Integer(), default=0) question = db.ManyToOne(Question, backref=db.backref("choices", cascade="all, delete-orphan")) Now that we have some models, lets create a migration using ``alembic`` integration: .. code:: console $ python manage.py sorcery revision -m "Add question and poll models" polls Generating ./polls/migrations/3983fc419e10_add_question_and_poll_models.py ... done Let's take a look at the generated migration file ``./polls/migrations/3983fc419e10_add_question_and_poll_models.py``: .. code:: python """ Add question and poll models Revision ID: 3983fc419e10 Revises: Create Date: 2019-04-16 20:57:48.154179 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '3983fc419e10' down_revision = None branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table('question', sa.Column('pk', sa.Integer(), autoincrement=True, nullable=False), sa.Column('question_text', sa.String(length=200), nullable=True), sa.Column('pub_date', sa.DateTime(), nullable=True), sa.PrimaryKeyConstraint('pk') ) op.create_table('choice', sa.Column('pk', sa.Integer(), autoincrement=True, nullable=False), sa.Column('choice_text', sa.String(length=200), nullable=True), sa.Column('votes', sa.Integer(), nullable=True), sa.Column('question_pk', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['question_pk'], ['question.pk'], ), sa.PrimaryKeyConstraint('pk') ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_table('choice') op.drop_table('question') # ### end Alembic commands ### Let's take a look at generated sql: .. code:: console $ python manage.py sorcery upgrade --sql polls CREATE TABLE alembic_version_polls ( version_num VARCHAR(32) NOT NULL, CONSTRAINT alembic_version_polls_pkc PRIMARY KEY (version_num) ); -- Running upgrade -> d7d86e07cc8e CREATE TABLE question ( pk INTEGER NOT NULL, question_text VARCHAR(200), pub_date DATETIME, PRIMARY KEY (pk) ); CREATE TABLE choice ( pk INTEGER NOT NULL, choice_text VARCHAR(200), votes INTEGER, question_pk INTEGER, PRIMARY KEY (pk), FOREIGN KEY(question_pk) REFERENCES question (pk) ); INSERT INTO alembic_version_polls (version_num) VALUES ('d7d86e07cc8e'); Let's bring our db up to date: .. code:: console $ python manage.py sorcery upgrade Running migrations for polls on database default Right now, we have enough to hop in django shell: .. code:: console $ python manage.py shell >>> from polls.models import Choice, Question, db # Import the model classes and the db # we have no choices or questions in db yet >>> Choice.query.all() [] >>> Question.query.all() [] # Lets create a new question >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) >>> q Question(pk=None, pub_date=datetime.datetime(2018, 5, 19, 0, 54, 20, 778186, tzinfo=<UTC>), question_text="What's new?") # lets save our question, we need to add our question to the db >>> db.add(q) # at this point the question is in pending state >>> db.new IdentitySet([Question(pk=None, pub_date=datetime.datetime(2018, 5, 19, 0, 54, 20, 778186, tzinfo=<UTC>), question_text="What's new?")]) # lets flush to the database >>> db.flush() # at this point our question is in persistent state and will receive a primary key >>> q.pk 1 # lets change the question text >>> q.question_text = "What's up?" >>> db.flush() # Question.objects and Question.query are both query properties that return a query object bound to db >>> Question.objects <django_sorcery.db.query.Query at 0x7feb1c7899e8> >>> Question.query <django_sorcery.db.query.Query at 0x7feb1c9377f0> # and lets see all the questions >>> Question.objects.all() [Question(pk=1, pub_date=datetime.datetime(2018, 5, 19, 0, 54, 20, 778186, tzinfo=<UTC>), question_text="What's up?")] >>> exit() Let's add a couple of views in ``polls/views.py``, starting with a list view: .. code:: python from django.shortcuts import render from django.template import loader from django.http import HttpResponseRedirect from django.urls import reverse from django_sorcery.shortcuts import get_object_or_404 from .models import Question, Choice, db def index(request): latest_question_list = Question.objects.order_by(Question.pub_date.desc())[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context) def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question}) def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question}) def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) selected_choice = Choice.query.filter( Choice.question == question, Choice.pk == request.POST['choice'], ).one_or_none() if not selected_choice: return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) selected_choice.votes += 1 db.flush() return HttpResponseRedirect(reverse('polls:results', args=(question.pk,))) and register the view in ``polls/urls.py``: .. code:: python from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results', views.results, name='results'), path('<int:question_id>/vote', views.vote, name='vote'), ] and register the ``SQLAlchemyMiddleware`` to provide unit-of-work per request pattern: .. code:: python MIDDLEWARE = [ 'django_sorcery.db.middleware.SQLAlchemyMiddleware', # ... ] and add some templates: ``polls/templates/polls/index.html``: .. code:: html {% if latest_question_list %} <ul> {% for question in latest_question_list %} <li><a href="{% url 'polls:detail' question.pk %}">{{ question.question_text }}</a></li> {% endfor %} </ul> {% else %} <p>No polls are available.</p> {% endif %} ``polls/templates/polls/detail.html``: .. code:: html <h1>{{ question.question_text }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' question.pk %}" method="post"> {% csrf_token %} {% for choice in question.choices %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.pk }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form> ``polls/templates/polls/results.html``: .. code:: html <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choices %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' question.pk %}">Vote again?</a> This is all fine but we can do one better using generic views. Lets adjust our views in ``polls/views.py``: .. code:: python from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse from django_sorcery.shortcuts import get_object_or_404 from django_sorcery import views from .models import Question, Choice, db class IndexView(views.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list' def get_queryset(self): return Question.objects.order_by(Question.pub_date.desc())[:5] class DetailView(views.DetailView): model = Question session = db template_name = 'polls/detail.html' class ResultsView(DetailView): template_name = 'polls/results.html' def vote(request, question_id): question = get_object_or_404(Question, pk=question_id) selected_choice = Choice.query.filter( Choice.question == question, Choice.pk == request.POST['choice'], ).one_or_none() if not selected_choice: return render(request, 'polls/detail.html', { 'question': question, 'error_message': "You didn't select a choice.", }) selected_choice.votes += 1 db.flush() return HttpResponseRedirect(reverse('polls:results', args=(question.pk,))) and adjust the ``polls/urls.py`` like: .. code:: python from django.urls import path from . import views app_name = 'polls' urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), path('<int:pk>/results', views.ResultsView.as_view(), name='results'), path('<int:question_id>/vote', views.vote, name='vote'), ] The default values for ``template_name`` and ``context_object_name`` are similar to django's generic views. If we handn't defined those the default for template names would've been ``polls/question_detail.html`` and ``polls/question_list.html`` for the detail and list template names, and ``question`` and ``question_list`` for context names for detail and list views. This is all fine but we can even do one better using a viewset. Lets adjust our views in ``polls/views.py``: .. code:: python from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy from django_sorcery.routers import action from django_sorcery.viewsets import ModelViewSet from .models import Question, Choice, db class PollsViewSet(ModelViewSet): model = Question fields = "__all__" destroy_success_url = reverse_lazy("polls:question-list") def get_success_url(self): return reverse("polls:question-detail", kwargs={"pk": self.object.pk}) @action(detail=True) def results(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) @action(detail=True, methods=["POST"]) def vote(self, request, *args, **kwargs): self.object = self.get_object() selected_choice = Choice.query.filter( Choice.question == self.object, Choice.pk == request.POST.get("choice") ).one_or_none() if not selected_choice: context = self.get_detail_context_data(object=self.object) context["error_message"] = "You didn't select a choice." self.action = "retrieve" return self.render_to_response(context) selected_choice.votes += 1 db.flush() return HttpResponseRedirect(reverse("polls:question-results", args=(self.object.pk,))) And adjusting our ``polls/urls.py`` like: .. code:: python from django.urls import path, include from django_sorcery.routers import SimpleRouter from . import views router = SimpleRouter() router.register("", views.PollsViewSet) app_name = "polls" urlpatterns = [path("", include(router.urls))] With these changes we'll have the following urls: .. code:: console $ ./manage.py run show_urls /polls/ polls.views.PollsViewSet polls:question-list /polls/<pk>/ polls.views.PollsViewSet polls:question-detail /polls/<pk>/delete/ polls.views.PollsViewSet polls:question-destroy /polls/<pk>/edit/ polls.views.PollsViewSet polls:question-edit /polls/<pk>/results/ polls.views.PollsViewSet polls:question-results /polls/<pk>/vote/ polls.views.PollsViewSet polls:question-vote /polls/new/ polls.views.PollsViewSet polls:question-new This will map the following operations to following actions on the viewset: ====== ======================== =============== =============== Method Path Action Route Name ====== ======================== =============== =============== GET /polls/ list question-list POST /polls/ create question-list GET /polls/new/ new question-new GET /polls/1/ retrieve question-detail POST /polls/1/ update question-detail PUT /polls/1/ update question-detail PATCH /polls/1/ update question-detail DELETE /polls/1/ destroy question-detail GET /polls/1/edit/ edit question-edit GET /polls/1/delete/ confirm_destoy question-delete POST /polls/1/delete/ destroy question-delete ====== ======================== =============== =============== Now, lets add an inline formset to be able to add choices to questions, adjust ``polls/views.py``: .. code:: python from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy from django_sorcery.routers import action from django_sorcery.viewsets import ModelViewSet from django_sorcery.formsets import inlineformset_factory from .models import Question, Choice, db ChoiceFormSet = inlineformset_factory(relation=Question.choices, fields=(Choice.choice_text.key,), session=db) class PollsViewSet(ModelViewSet): model = Question fields = (Question.question_text.key, Question.pub_date.key) destroy_success_url = reverse_lazy("polls:question-list") def get_success_url(self): return reverse("polls:question-detail", kwargs={"pk": self.object.pk}) def get_form_context_data(self, **kwargs): kwargs["choice_formset"] = self.get_choice_formset() return super().get_form_context_data(**kwargs) def get_choice_formset(self, instance=None): if not hasattr(self, "_choice_formset"): instance = instance or self.object self._choice_formset = ChoiceFormSet( instance=instance, data=self.request.POST if self.request.POST else None ) return self._choice_formset def process_form(self, form): if form.is_valid() and self.get_choice_formset(instance=form.instance).is_valid(): return self.form_valid(form) return form.invalid(self, form) def form_valid(self, form): self.object = form.save() self.object.choices = self.get_choice_formset().save() db.flush() return HttpResponseRedirect(self.get_success_url()) @action(detail=True) def results(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) @action(detail=True, methods=["POST"]) def vote(self, request, *args, **kwargs): self.object = self.get_object() selected_choice = Choice.query.filter( Choice.question == self.object, Choice.pk == request.POST.get("choice") ).one_or_none() if not selected_choice: context = self.get_detail_context_data(object=self.object) context["error_message"] = "You didn't select a choice." self.action = "retrieve" return self.render_to_response(context) selected_choice.votes += 1 db.flush() return HttpResponseRedirect(reverse("polls:question-results", args=(self.object.pk,))) And add ``choice_formset`` in the ``polls/templates/question_edit.html`` and ``polls/templates/question_edit.html`` .. code:: html <form ... > ... {{ choice_formset }} ... </form > .. |Build Status| image:: https://github.com/shosca/django-sorcery/workflows/Build/badge.svg?branch=master :target: https://github.com/shosca/django-sorcery/actions?query=workflow%3ABuild+branch%3Amaster .. |Read The Docs| image:: https://readthedocs.org/projects/django-sorcery/badge/?version=latest :target: http://django-sorcery.readthedocs.io/en/latest/?badge=latest .. |PyPI version| image:: https://badge.fury.io/py/django-sorcery.svg :target: https://badge.fury.io/py/django-sorcery .. |Coveralls Status| image:: https://coveralls.io/repos/github/shosca/django-sorcery/badge.svg?branch=master :target: https://coveralls.io/github/shosca/django-sorcery?branch=master .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/ambv/black


نیازمندی

مقدار نام
- alembic
- blinker
- inflect
- django
- python-dateutil
<2 sqlalchemy


نحوه نصب


نصب پکیج whl django-sorcery-0.9.4:

    pip install django-sorcery-0.9.4.whl


نصب پکیج tar.gz django-sorcery-0.9.4:

    pip install django-sorcery-0.9.4.tar.gz